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 095c4c6  Support license expression in dep check. (#120)
095c4c6 is described below

commit 095c4c644a4ab3ef3cb03533416de23e8f8150ab
Author: jmjoy <[email protected]>
AuthorDate: Thu Jul 7 10:56:12 2022 +0800

    Support license expression in dep check. (#120)
---
 pkg/deps/check.go      | 102 ++++++++++++++++++++++++++++++----
 pkg/deps/check_test.go | 148 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 240 insertions(+), 10 deletions(-)

diff --git a/pkg/deps/check.go b/pkg/deps/check.go
index d00a1f2..15bc546 100644
--- a/pkg/deps/check.go
+++ b/pkg/deps/check.go
@@ -28,12 +28,21 @@ import (
        "github.com/apache/skywalking-eyes/internal/logger"
 )
 
-type compatibilityMatrix struct {
+type CompatibilityMatrix struct {
        Compatible   []string `yaml:"compatible"`
        Incompatible []string `yaml:"incompatible"`
 }
 
-var matrices = make(map[string]compatibilityMatrix)
+var matrices = make(map[string]CompatibilityMatrix)
+
+type LicenseOperator int
+
+const (
+       LicenseOperatorNone LicenseOperator = iota
+       LicenseOperatorAND
+       LicenseOperatorOR
+       LicenseOperatorWITH
+)
 
 func init() {
        dir := "compatibility"
@@ -43,7 +52,7 @@ func init() {
        }
        for _, file := range files {
                name := file.Name()
-               matrix := compatibilityMatrix{}
+               matrix := CompatibilityMatrix{}
                if bytes, err := assets.Asset(filepath.Join(dir, name)); err != 
nil {
                        logger.Log.Fatalln("Failed to read compatibility 
file:", name, err)
                } else if err := yaml.Unmarshal(bytes, &matrix); err != nil {
@@ -54,27 +63,78 @@ func init() {
 }
 
 func Check(mainLicenseSpdxID string, config *ConfigDeps) error {
+       matrix := matrices[mainLicenseSpdxID]
+
        report := Report{}
        if err := Resolve(config, &report); err != nil {
                return nil
        }
 
-       matrix := matrices[mainLicenseSpdxID]
+       return CheckWithMatrix(mainLicenseSpdxID, &matrix, &report)
+}
+
+func CheckWithMatrix(mainLicenseSpdxID string, matrix *CompatibilityMatrix, 
report *Report) error {
        var incompatibleResults []*Result
        for _, result := range append(report.Resolved, report.Skipped...) {
-               compare := func(list []string) bool {
+               compare := func(list []string, spdxID string) bool {
                        for _, com := range list {
-                               if result.LicenseSpdxID == com {
+                               if spdxID == com {
                                        return true
                                }
                        }
                        return false
                }
-               if compatible := compare(matrix.Compatible); compatible {
-                       continue
+               compareAll := func(spdxIDs []string, compare func(spdxID 
string) bool) bool {
+                       for _, spdxID := range spdxIDs {
+                               if !compare(spdxID) {
+                                       return false
+                               }
+                       }
+                       return true
                }
-               if incompatible := compare(matrix.Incompatible); incompatible {
-                       incompatibleResults = append(incompatibleResults, 
result)
+               compareAny := func(spdxIDs []string, compare func(spdxID 
string) bool) bool {
+                       for _, spdxID := range spdxIDs {
+                               if compare(spdxID) {
+                                       return true
+                               }
+                       }
+                       return false
+               }
+
+               operator, spdxIDs := 
parseLicenseExpression(result.LicenseSpdxID)
+
+               switch operator {
+               case LicenseOperatorAND:
+                       if compareAll(spdxIDs, func(spdxID string) bool {
+                               return compare(matrix.Compatible, spdxID)
+                       }) {
+                               continue
+                       }
+                       if compareAny(spdxIDs, func(spdxID string) bool {
+                               return compare(matrix.Incompatible, spdxID)
+                       }) {
+                               incompatibleResults = 
append(incompatibleResults, result)
+                       }
+
+               case LicenseOperatorOR:
+                       if compareAny(spdxIDs, func(spdxID string) bool {
+                               return compare(matrix.Compatible, spdxID)
+                       }) {
+                               continue
+                       }
+                       if compareAll(spdxIDs, func(spdxID string) bool {
+                               return compare(matrix.Incompatible, spdxID)
+                       }) {
+                               incompatibleResults = 
append(incompatibleResults, result)
+                       }
+
+               default:
+                       if compatible := compare(matrix.Compatible, 
spdxIDs[0]); compatible {
+                               continue
+                       }
+                       if incompatible := compare(matrix.Incompatible, 
spdxIDs[0]); incompatible {
+                               incompatibleResults = 
append(incompatibleResults, result)
+                       }
                }
        }
 
@@ -88,3 +148,25 @@ func Check(mainLicenseSpdxID string, config *ConfigDeps) 
error {
 
        return nil
 }
+
+func parseLicenseExpression(s string) (operator LicenseOperator, spdxIDs 
[]string) {
+       if ss := strings.Split(s, " AND "); len(ss) > 1 {
+               return LicenseOperatorAND, ss
+       }
+       if ss := strings.Split(s, " and "); len(ss) > 1 {
+               return LicenseOperatorAND, ss
+       }
+       if ss := strings.Split(s, " OR "); len(ss) > 1 {
+               return LicenseOperatorOR, ss
+       }
+       if ss := strings.Split(s, " or "); len(ss) > 1 {
+               return LicenseOperatorOR, ss
+       }
+       if ss := strings.Split(s, " WITH "); len(ss) > 1 {
+               return LicenseOperatorWITH, ss
+       }
+       if ss := strings.Split(s, " with "); len(ss) > 1 {
+               return LicenseOperatorWITH, ss
+       }
+       return LicenseOperatorNone, []string{s}
+}
diff --git a/pkg/deps/check_test.go b/pkg/deps/check_test.go
new file mode 100644
index 0000000..2ba66a4
--- /dev/null
+++ b/pkg/deps/check_test.go
@@ -0,0 +1,148 @@
+// 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_test
+
+import (
+       "github.com/apache/skywalking-eyes/pkg/deps"
+       "strings"
+       "testing"
+)
+
+var TestMatrix = deps.CompatibilityMatrix{
+       Compatible: []string{
+               "Apache-2.0",
+               "PHP-3.01",
+               "BSD-3-Clause",
+               "BSD-2-Clause",
+               "PostgreSQL",
+               "EPL-1.0",
+               "ISC",
+       },
+       Incompatible: []string{
+               "Unknown",
+               "LGPL-2.0+",
+               "LGPL-2.0",
+               "LGPL-2.0-only",
+               "LGPL-2.0-or-later",
+               "LGPL-2.1+",
+               "LGPL-2.1",
+               "LGPL-2.1-only",
+               "LGPL-2.1-or-later",
+               "LGPL-3.0+",
+               "LGPL-3.0",
+               "GPL-3.0+",
+               "GPL-3.0",
+               "GPL-2.0+",
+               "GPL-2.0",
+               "GPL-2.0-only",
+               "GPL-2.0-or-later",
+       },
+}
+
+func TestCheckWithMatrix(t *testing.T) {
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "Apache-2.0",
+                       },
+               },
+       }); err != nil {
+               t.Errorf("Shouldn't return error")
+       }
+
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "Apache-2.0",
+                       },
+                       {
+                               Dependency:    "Bar",
+                               LicenseSpdxID: "LGPL-2.0",
+                       },
+               },
+       }); err == nil {
+               t.Errorf("Should return error")
+       } else if !strings.Contains(err.Error(), "License: LGPL-2.0 Dependency: 
Bar") {
+               t.Errorf("Should return error and contains dependency Bar, now 
is `%s`", err.Error())
+       }
+
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "Apache-2.0",
+                       },
+               },
+               Skipped: []*deps.Result{
+                       {
+                               Dependency:    "Bar",
+                               LicenseSpdxID: "Unknown",
+                       },
+               },
+       }); err == nil {
+               t.Errorf("Should return error")
+       } else if !strings.Contains(err.Error(), "License: Unknown Dependency: 
Bar") {
+               t.Errorf("Should return error and has dependency Bar, now is 
`%s`", err.Error())
+       }
+
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "Apache-2.0 OR MIT",
+                       },
+               },
+       }); err != nil {
+               t.Errorf("Shouldn't return error")
+       }
+
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "GPL-3.0 and GPL-3.0-or-later",
+                       },
+               },
+       }); err == nil {
+               t.Errorf("Should return error")
+       }
+
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "LGPL-2.1-only AND MIT AND 
BSD-2-Clause",
+                       },
+               },
+       }); err == nil {
+               t.Errorf("Should return error")
+       }
+
+       if err := deps.CheckWithMatrix("Apache-2.0", &TestMatrix, &deps.Report{
+               Resolved: []*deps.Result{
+                       {
+                               Dependency:    "Foo",
+                               LicenseSpdxID: "GPL-2.0-or-later WITH 
Bison-exception-2.2",
+                       },
+               },
+       }); err == nil {
+               t.Errorf("Should return error")
+       }
+}

Reply via email to