Refactor the normalizers to increase accuracy and minimise the use of pattern (#4)
diff --git a/.golangci.yml b/.golangci.yml
index f4a7795..f4a6fee 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -16,6 +16,9 @@
# under the License.
#
+run:
+ tests: false
+
linters-settings:
govet:
check-shadowing: true
@@ -37,6 +40,38 @@
fmt: "logging is allowed only by logutils.Log"
misspell:
locale: US
+ ignore-words:
+ - analogue
+ - analyse
+ - artefact
+ - authorised
+ - calibre
+ - cancelled
+ - catalogue
+ - categorise
+ - centre
+ - emphasised
+ - favour
+ - favourite
+ - fulfil
+ - fulfilment
+ - initialise
+ - labelling
+ - labour
+ - licence
+ - maximise
+ - modelled
+ - modelling
+ - offence
+ - optimise
+ - organisation
+ - organise
+ - practise
+ - programme
+ - realise
+ - recognise
+ - signalling
+ - utilisation
lll:
line-length: 150
goimports:
@@ -51,7 +86,7 @@
disabled-checks:
- ifElseChain
funlen:
- lines: 150
+ lines: 100
statements: 50
whitespace:
multi-if: false
diff --git a/.licenserc.yaml b/.licenserc.yaml
index e4494c7..819f2b1 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -38,10 +38,10 @@
# `pattern` is optional regexp if all the file headers are the same as `license` (linebreaks doesn't matter);
# In the `pattern`, all punctuations should be removed unless they are part of the regex;
pattern: |
- Licensed to( the)? Apache Software Foundation \(ASF\) under one or more contributor
+ Licensed to the Apache Software Foundation 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\)|The ASF) licenses this file to you under
+ ownership. The Apache Software Foundation 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
diff --git a/license-eye/commands/header/check.go b/license-eye/commands/header/check.go
index c349ace..0578286 100644
--- a/license-eye/commands/header/check.go
+++ b/license-eye/commands/header/check.go
@@ -19,6 +19,7 @@
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"
@@ -31,7 +32,7 @@
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 header.Result
+ var result pkg.Result
if err := config.Parse(cfgFile); err != nil {
return err
diff --git a/license-eye/commands/header/fix.go b/license-eye/commands/header/fix.go
index b65069f..4fbba48 100644
--- a/license-eye/commands/header/fix.go
+++ b/license-eye/commands/header/fix.go
@@ -23,6 +23,7 @@
"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,7 +35,7 @@
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 header.Result
+ var result pkg.Result
if err := config.Parse(cfgFile); err != nil {
return err
diff --git a/license-eye/internal/logger/log.go b/license-eye/internal/logger/log.go
index 1c641e8..7fc102e 100644
--- a/license-eye/internal/logger/log.go
+++ b/license-eye/internal/logger/log.go
@@ -29,7 +29,7 @@
if Log == nil {
Log = logrus.New()
}
- Log.Level = logrus.InfoLevel
+ Log.Level = logrus.DebugLevel
Log.SetOutput(os.Stdout)
Log.SetFormatter(&logrus.TextFormatter{
DisableTimestamp: true,
diff --git a/license-eye/pkg/header/check.go b/license-eye/pkg/header/check.go
index 8053cd3..c32279b 100644
--- a/license-eye/pkg/header/check.go
+++ b/license-eye/pkg/header/check.go
@@ -18,7 +18,6 @@
package header
import (
- "bufio"
"io/ioutil"
"net/http"
"os"
@@ -27,20 +26,20 @@
"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"
)
-// TODO: also trim stop words
var (
// LicenseLocationThreshold specifies the index threshold where the license header can be located,
// after all, a "header" cannot be TOO far from the file start.
LicenseLocationThreshold = 80
- Punctuations = regexp.MustCompile("[\\[\\]/*:;\\s#\\-!~'\"(){}?]+")
)
// Check checks the license headers of the specified paths/globs.
-func Check(config *ConfigHeader, result *Result) error {
+func Check(config *ConfigHeader, result *pkg.Result) error {
for _, pattern := range config.Paths {
if err := checkPattern(pattern, result, config); err != nil {
return err
@@ -52,7 +51,7 @@
var seen = make(map[string]bool)
-func checkPattern(pattern string, result *Result, config *ConfigHeader) error {
+func checkPattern(pattern string, result *pkg.Result, config *ConfigHeader) error {
paths, err := doublestar.Glob(pattern)
if err != nil {
@@ -73,7 +72,7 @@
return nil
}
-func checkPath(path string, result *Result, config *ConfigHeader) error {
+func checkPath(path string, result *pkg.Result, config *ConfigHeader) error {
defer func() { seen[path] = true }()
if yes, err := config.ShouldIgnore(path); yes || seen[path] || err != nil {
@@ -106,7 +105,7 @@
}
// CheckFile checks whether or not the file contains the configured license header.
-func CheckFile(file string, config *ConfigHeader, result *Result) error {
+func CheckFile(file string, config *ConfigHeader, result *pkg.Result) error {
if yes, err := config.ShouldIgnore(file); yes || err != nil {
if !seen[file] {
result.Ignore(file)
@@ -116,14 +115,6 @@
logger.Log.Debugln("Checking file:", file)
- reader, err := os.Open(file)
-
- if err != nil {
- return err
- }
-
- var lines []string
-
bs, err := ioutil.ReadFile(file)
if err != nil {
return err
@@ -133,24 +124,13 @@
return nil
}
- scanner := bufio.NewScanner(reader)
- for scanner.Scan() {
- line := strings.ToLower(Punctuations.ReplaceAllString(scanner.Text(), " "))
- if len(line) > 0 {
- lines = append(lines, line)
- }
- }
+ content := lcs.NormalizeHeader(string(bs))
+ expected, pattern := config.NormalizedLicense(), config.NormalizedPattern()
- content := Punctuations.ReplaceAllString(strings.Join(lines, " "), " ")
- license, pattern := config.NormalizedLicense(), config.NormalizedPattern()
-
- if satisfy(content, license, pattern) {
+ if satisfy(content, expected, pattern) {
result.Succeed(file)
} else {
logger.Log.Debugln("Content is:", content)
- if pattern != nil {
- logger.Log.Debugln("Pattern is:", pattern)
- }
result.Fail(file)
}
@@ -159,7 +139,7 @@
}
func satisfy(content, license string, pattern *regexp.Regexp) bool {
- if index := strings.Index(content, license); index >= 0 {
+ if index := strings.Index(content, license); license != "" && index >= 0 {
return index < LicenseLocationThreshold
}
diff --git a/license-eye/pkg/header/check_test.go b/license-eye/pkg/header/check_test.go
index 2cc2de7..ca8e1bb 100644
--- a/license-eye/pkg/header/check_test.go
+++ b/license-eye/pkg/header/check_test.go
@@ -24,6 +24,7 @@
"strings"
"testing"
+ "github.com/apache/skywalking-eyes/license-eye/pkg"
"gopkg.in/yaml.v3"
)
@@ -48,7 +49,7 @@
type args struct {
name string
file string
- result *Result
+ result *pkg.Result
wantErr bool
hasFailure bool
}
@@ -64,7 +65,7 @@
cases = append(cases, args{
name: file,
file: file,
- result: &Result{},
+ result: &pkg.Result{},
wantErr: false,
hasFailure: false,
})
@@ -99,7 +100,7 @@
type args struct {
name string
file string
- result *Result
+ result *pkg.Result
wantErr bool
hasFailure bool
}
@@ -115,7 +116,7 @@
cases = append(cases, args{
name: file,
file: file,
- result: &Result{},
+ result: &pkg.Result{},
wantErr: false,
hasFailure: true,
})
diff --git a/license-eye/pkg/header/config.go b/license-eye/pkg/header/config.go
index 8911cfc..d303dd4 100644
--- a/license-eye/pkg/header/config.go
+++ b/license-eye/pkg/header/config.go
@@ -19,15 +19,14 @@
import (
"bufio"
- "io/ioutil"
"os"
"regexp"
"strings"
"github.com/apache/skywalking-eyes/license-eye/internal/logger"
+ "github.com/apache/skywalking-eyes/license-eye/pkg/license"
"github.com/bmatcuk/doublestar/v2"
- "gopkg.in/yaml.v3"
)
type ConfigHeader struct {
@@ -40,47 +39,19 @@
// NormalizedLicense returns the normalized string of the license content,
// "normalized" means the linebreaks and Punctuations are all trimmed.
func (config *ConfigHeader) NormalizedLicense() string {
- var lines []string
- for _, line := range strings.Split(config.License, "\n") {
- if len(line) > 0 {
- lines = append(lines, Punctuations.ReplaceAllString(line, " "))
- }
- }
- return strings.ToLower(regexp.MustCompile("(?m)[\\s\"']+").ReplaceAllString(strings.Join(lines, " "), " "))
+ return license.Normalize(config.License)
}
func (config *ConfigHeader) NormalizedPattern() *regexp.Regexp {
- if config.Pattern == "" || strings.TrimSpace(config.Pattern) == "" {
+ pattern := config.Pattern
+
+ if pattern == "" || strings.TrimSpace(pattern) == "" {
return nil
}
- var lines []string
- for _, line := range strings.Split(config.Pattern, "\n") {
- if len(line) > 0 {
- lines = append(lines, line)
- }
- }
- content := regexp.MustCompile("(?m)[\\s\"':;/\\-]+").ReplaceAllString(strings.Join(lines, " "), " ")
- return regexp.MustCompile("(?i).*" + content + ".*")
-}
+ pattern = license.NormalizePattern(pattern)
-// Parse reads and parses the header check configurations in config file.
-func (config *ConfigHeader) 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
- }
-
- logger.Log.Debugln("License header is:", config.NormalizedLicense())
-
- if len(config.Paths) == 0 {
- config.Paths = []string{"**"}
- }
-
- return nil
+ return regexp.MustCompile("(?i).*" + pattern + ".*")
}
func (config *ConfigHeader) ShouldIgnore(path string) (bool, error) {
@@ -103,8 +74,6 @@
}
func (config *ConfigHeader) Finalize() error {
- logger.Log.Debugln("License header is:", config.NormalizedLicense())
-
if len(config.Paths) == 0 {
config.Paths = []string{"**"}
}
@@ -124,5 +93,10 @@
}
}
+ logger.Log.Debugln("License header is:", config.NormalizedLicense())
+ if p := config.NormalizedPattern(); p != nil {
+ logger.Log.Debugln("Pattern is:", p)
+ }
+
return nil
}
diff --git a/license-eye/pkg/header/fix.go b/license-eye/pkg/header/fix.go
index 40acde7..203de06 100644
--- a/license-eye/pkg/header/fix.go
+++ b/license-eye/pkg/header/fix.go
@@ -26,12 +26,13 @@
"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 *Result) error {
- var r Result
+func Fix(file string, config *ConfigHeader, result *pkg.Result) error {
+ var r pkg.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
@@ -50,7 +51,7 @@
return nil
}
-func InsertComment(file string, style *comments.CommentStyle, config *ConfigHeader, result *Result) error {
+func InsertComment(file string, style *comments.CommentStyle, config *ConfigHeader, result *pkg.Result) error {
stat, err := os.Stat(file)
if err != nil {
return err
diff --git a/license-eye/pkg/license/norm.go b/license-eye/pkg/license/norm.go
new file mode 100644
index 0000000..771f2b2
--- /dev/null
+++ b/license-eye/pkg/license/norm.go
@@ -0,0 +1,171 @@
+//
+// 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 license
+
+import (
+ "regexp"
+ "strings"
+)
+
+type Normalizer func(string) string
+
+var (
+ // normalizers is a list of Normalizer that can be applied to the license text, yet doesn't change the license's
+ // meanings, according to the matching guide in https://spdx.dev/license-list/matching-guidelines.
+ // The order matters.
+ normalizers = []Normalizer{
+ OneLineNormalizer,
+ FlattenSpaceNormalizer,
+ SubstantiveTextsNormalizer,
+ strings.ToLower,
+ strings.TrimSpace,
+ }
+
+ // 6. Code Comment Indicators (https://spdx.dev/license-list/matching-guidelines.)
+ commentIndicators = []*regexp.Regexp{
+ regexp.MustCompile(`(?m)^\s*#+`), // #
+ regexp.MustCompile(`(?m)^\s*//+`), // //
+ regexp.MustCompile(`(?m)^\s*"""+`), // """
+ regexp.MustCompile(`(?m)^\s*\(\*+`), // (*
+
+ regexp.MustCompile(`(?m)^\s*/\*+`), // /*
+ regexp.MustCompile(`(?m)^\s*\*+/`), // */
+ regexp.MustCompile(`(?m)^\s*\*+`), // *
+
+ regexp.MustCompile(`(?m)^\s*<!--+`), // <!--
+ regexp.MustCompile(`(?m)^\s*--+>`), // -->
+ regexp.MustCompile(`(?m)^\s*--+`), // --
+ regexp.MustCompile(`(?m)^\s*~+`), // ~
+
+ regexp.MustCompile(`(?m)^\s*{-+`), // {-
+ regexp.MustCompile(`(?m)^\s*-}+`), // -}
+
+ regexp.MustCompile(`(?m)^\s*::`), // ::
+ regexp.MustCompile(`(?m)^\s*@REM`), // @REM
+ }
+
+ flattenSpace = regexp.MustCompile(`\s+`)
+
+ substitutableTexts = []struct {
+ regex *regexp.Regexp
+ replacement string
+ }{
+ {regexp.MustCompile(`(?i)\backnowledgement\b`), "acknowledgment"},
+ {regexp.MustCompile(`(?i)\banalog\b`), "analogue"},
+ {regexp.MustCompile(`(?i)\banalyze\b`), "analyse"},
+ {regexp.MustCompile(`(?i)\bartifact\b`), "artefact"},
+ {regexp.MustCompile(`(?i)\bauthorization\b`), "authorisation"},
+ {regexp.MustCompile(`(?i)\bauthorized\b`), "authorised"},
+ {regexp.MustCompile(`(?i)\bcaliber\b`), "calibre"},
+ {regexp.MustCompile(`(?i)\bcanceled\b`), "cancelled"},
+ {regexp.MustCompile(`(?i)\bcapitalizations\b`), "capitalisations"},
+ {regexp.MustCompile(`(?i)\bcatalog\b`), "catalogue"},
+ {regexp.MustCompile(`(?i)\bcategorize\b`), "categorise"},
+ {regexp.MustCompile(`(?i)\bcenter\b`), "centre"},
+ {regexp.MustCompile(`(?i)\bcopyright holder\b`), "copyright owner"},
+ {regexp.MustCompile(`(?i)\bemphasized\b`), "emphasised"},
+ {regexp.MustCompile(`(?i)\bfavor\b`), "favour"},
+ {regexp.MustCompile(`(?i)\bfavorite\b`), "favourite"},
+ {regexp.MustCompile(`(?i)\bfulfill\b`), "fulfil"},
+ {regexp.MustCompile(`(?i)\bfulfillment\b`), "fulfilment"},
+ {regexp.MustCompile(`(?i)\binitialize\b`), "initialise"},
+ {regexp.MustCompile(`(?i)\bjudgement\b`), "judgment"},
+ {regexp.MustCompile(`(?i)\blabeling\b`), "labelling"},
+ {regexp.MustCompile(`(?i)\blabor\b`), "labour"},
+ {regexp.MustCompile(`(?i)\blicense\b`), "licence"},
+ {regexp.MustCompile(`(?i)\bmaximize\b`), "maximise"},
+ {regexp.MustCompile(`(?i)\bmodeled\b`), "modelled"},
+ {regexp.MustCompile(`(?i)\bmodeling\b`), "modelling"},
+ {regexp.MustCompile(`(?i)\bnoncommercial\b`), "non-commercial"},
+ {regexp.MustCompile(`(?i)\boffense\b`), "offence"},
+ {regexp.MustCompile(`(?i)\boptimize\b`), "optimise"},
+ {regexp.MustCompile(`(?i)\borganization\b`), "organisation"},
+ {regexp.MustCompile(`(?i)\borganize\b`), "organise"},
+ {regexp.MustCompile(`(?i)\bpercent\b`), "per cent"},
+ {regexp.MustCompile(`(?i)\bpractice\b`), "practise"},
+ {regexp.MustCompile(`(?i)\bprogram\b`), "programme"},
+ {regexp.MustCompile(`(?i)\brealize\b`), "realise"},
+ {regexp.MustCompile(`(?i)\brecognize\b`), "recognise"},
+ {regexp.MustCompile(`(?i)\bsignaling\b`), "signalling"},
+ {regexp.MustCompile(`(?i)\bsublicense\b`), "sub-license"},
+ {regexp.MustCompile(`(?i)\bsub-license\b`), "sub license"},
+ {regexp.MustCompile(`(?i)\bsublicense\b`), "sub license"},
+ {regexp.MustCompile(`(?i)\butilization\b`), "utilisation"},
+ {regexp.MustCompile(`(?i)\bwhile\b`), "whilst"},
+ {regexp.MustCompile(`(?i)\bwilfull\b`), "wilful"},
+
+ {regexp.MustCompile(`©`), "Copyright "},
+ {regexp.MustCompile(`\(c\)`), "Copyright "},
+ {regexp.MustCompile(`\bhttps://`), "http://"},
+
+ {regexp.MustCompile(`(?i)\b(the )?Apache Software Foundation( \(ASF\))?`), "the ASF"},
+ }
+)
+
+// NormalizePattern applies a chain of Normalizers to the license pattern to make it cleaner for identification.
+func NormalizePattern(pattern string) string {
+ for _, normalize := range normalizers {
+ pattern = normalize(pattern)
+ }
+ return pattern
+}
+
+// NormalizeHeader applies a chain of Normalizers to the file header to make it cleaner for identification.
+func NormalizeHeader(header string) string {
+ ns := append([]Normalizer{CommentIndicatorNormalizer}, normalizers...)
+ for _, normalize := range ns {
+ header = normalize(header)
+ }
+ return header
+}
+
+// Normalize applies a chain of Normalizers to the license text to make it cleaner for identification.
+func Normalize(license string) string {
+ for _, normalize := range normalizers {
+ license = normalize(license)
+ }
+ return license
+}
+
+// OneLineNormalizer simply removes all line breaks to flatten the license text into one line.
+func OneLineNormalizer(text string) string {
+ return regexp.MustCompile("[\n\r]+").ReplaceAllString(text, " ")
+}
+
+// SubstantiveTextsNormalizer normalizes the license text by substituting some words that
+// doesn't change the meaning of the license.
+func SubstantiveTextsNormalizer(text string) string {
+ for _, s := range substitutableTexts {
+ text = s.regex.ReplaceAllString(text, s.replacement)
+ }
+ return text
+}
+
+// CommentIndicatorNormalizer trims the leading characters of comments, such as /*, <!--, --, (*, etc..
+func CommentIndicatorNormalizer(text string) string {
+ for _, leadingChars := range commentIndicators {
+ text = leadingChars.ReplaceAllString(text, "")
+ }
+ return text
+}
+
+// FlattenSpaceNormalizer flattens continuous spaces into a single space.
+func FlattenSpaceNormalizer(text string) string {
+ return flattenSpace.ReplaceAllString(text, " ")
+}
diff --git a/license-eye/pkg/license/norm_test.go b/license-eye/pkg/license/norm_test.go
new file mode 100644
index 0000000..fd9fbc5
--- /dev/null
+++ b/license-eye/pkg/license/norm_test.go
@@ -0,0 +1,260 @@
+//
+// 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 license
+
+import "testing"
+
+type input struct {
+ name string
+ text string
+ want string
+}
+
+func TestCommentLeadingCharsNormalizer(t *testing.T) {
+ want := ` 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.
+
+`
+ tests := []input{
+ {
+ name: "Jave",
+ want: want,
+ text: `
+/**
+ * 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.
+ */
+`,
+ },
+ {
+ name: "Python",
+ want: want,
+ text: `
+# 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.
+#
+`,
+ },
+ {
+ name: "XML",
+ want: want,
+ text: `
+<!--
+ ~ 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.
+-->
+`,
+ },
+ {
+ name: "GoLang",
+ want: want,
+ text: `
+// 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.
+//
+`,
+ },
+ {
+ name: "SQL",
+ want: want,
+ text: `
+-- 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.
+--
+`,
+ },
+ {
+ name: "BAT1",
+ want: want,
+ text: `
+:: 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.
+::
+`,
+ },
+ {
+ name: "BAT2",
+ want: want,
+ text: `
+@REM Licensed to the Apache Software Foundation (ASF) under one or more
+@REM contributor license agreements. See the NOTICE file distributed with
+@REM this work for additional information regarding copyright ownership.
+@REM The ASF licenses this file to You under the Apache License, Version 2.0
+@REM (the "License"); you may not use this file except in compliance with
+@REM the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+@REM
+`,
+ },
+ {
+ name: "PythonTripleQuotes",
+ text: `
+"""
+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.
+"""
+`,
+ want: `
+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.
+
+`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := CommentIndicatorNormalizer(tt.text); got != tt.want {
+ t.Errorf("%v %v", len(got), len(tt.want))
+ t.Errorf("CommentIndicatorNormalizer() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestSubstantiveTextsNormalizer(t *testing.T) {
+ tests := []input{
+ {
+ name: "ASF",
+ text: "Licensed to the Apache Software Foundation (ASF) under one or more",
+ want: "Licensed to the ASF under one or more",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := SubstantiveTextsNormalizer(tt.text); got != tt.want {
+ t.Errorf("SubstantiveTextsNormalizer() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/license-eye/pkg/header/result.go b/license-eye/pkg/result.go
similarity index 98%
rename from license-eye/pkg/header/result.go
rename to license-eye/pkg/result.go
index 068fe75..9333671 100644
--- a/license-eye/pkg/header/result.go
+++ b/license-eye/pkg/result.go
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
//
-package header
+package pkg
import (
"fmt"