You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@skywalking.apache.org by ke...@apache.org on 2020/12/22 13:46:14 UTC

[skywalking-eyes] branch enhance created (now b372537)

This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a change to branch enhance
in repository https://gitbox.apache.org/repos/asf/skywalking-eyes.git.


      at b372537  Append .gitignore content automatically, add `LicenseLocationThreshold`

This branch includes the following new commits:

     new 03d5afb  Enhance checking/fixing command, more tests, more comment styles
     new b372537  Append .gitignore content automatically, add `LicenseLocationThreshold`

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[skywalking-eyes] 01/02: Enhance checking/fixing command, more tests, more comment styles

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch enhance
in repository https://gitbox.apache.org/repos/asf/skywalking-eyes.git

commit 03d5afbb89e24a98c6082295eb18f2c056502fdf
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Tue Dec 22 18:12:07 2020 +0800

    Enhance checking/fixing command, more tests, more comment styles
    
    - `fix` command should properly tackle shebang and xml declaration;
    - Add more known comment styles;
    - Add more test cases;
    - Support checking a single file for debugging;
---
 .github/workflows/license-eye-check.yaml           |   3 +
 .gitignore                                         |   1 +
 .golangci.yml                                      |   2 +-
 license-eye/Makefile                               |   3 +-
 license-eye/assets/languages.yaml                  |   8 +
 license-eye/assets/styles.yaml                     |  31 ++-
 license-eye/commands/header/check.go               |   5 +
 license-eye/internal/logger/log.go                 |   2 +-
 license-eye/pkg/comments/config.go                 |  11 +-
 license-eye/pkg/config/{Config.go => config.go}    |   0
 license-eye/pkg/header/check.go                    |  13 +-
 license-eye/pkg/header/check_test.go               | 143 +++++++++++++
 license-eye/pkg/header/config.go                   |  14 +-
 license-eye/pkg/header/fix.go                      |  28 ++-
 license-eye/pkg/header/fix_test.go                 | 236 ++++++++++++++++-----
 .../test/testdata/.licenserc_for_test_check.yaml   |   2 +-
 .../test/testdata/.licenserc_for_test_fix.yaml     |   2 +-
 .../testdata/include_test/with_license/testcase.go |   3 +-
 .../include_test/with_license/testcase.java        |  11 +-
 .../testdata/include_test/with_license/testcase.ml |  19 ++
 .../include_test/without_license/testcase.py       |   1 +
 21 files changed, 441 insertions(+), 97 deletions(-)

diff --git a/.github/workflows/license-eye-check.yaml b/.github/workflows/license-eye-check.yaml
index cd40f8c..2bcfd23 100644
--- a/.github/workflows/license-eye-check.yaml
+++ b/.github/workflows/license-eye-check.yaml
@@ -50,3 +50,6 @@ jobs:
 
       - name: Build
         run: make build
+
+      - name: Build Docker Image
+        run: make docker
diff --git a/.gitignore b/.gitignore
index a559c8f..a90f1dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@
 bin/
 license-eye/assets/assets.gen.go
 .DS_Store
+coverage.txt
diff --git a/.golangci.yml b/.golangci.yml
index ca0227f..f4a7795 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -51,7 +51,7 @@ linters-settings:
     disabled-checks:
       - ifElseChain
   funlen:
-    lines: 100
+    lines: 150
     statements: 50
   whitespace:
     multi-if: false
diff --git a/license-eye/Makefile b/license-eye/Makefile
index 0629e37..09f9fca 100644
--- a/license-eye/Makefile
+++ b/license-eye/Makefile
@@ -61,7 +61,7 @@ codegen: clean
 
 .PHONY: test
 test: clean codegen
-	$(GO_TEST) ./...
+	$(GO_TEST) ./... -coverprofile=coverage.txt -covermode=atomic
 	@>&2 echo "Great, all tests passed."
 
 .PHONY: $(PLATFORMS)
@@ -80,3 +80,4 @@ docker:
 clean:
 	-rm -rf bin
 	-rm -rf assets/*.gen.go
+	-rm -rf coverage.txt
diff --git a/license-eye/assets/languages.yaml b/license-eye/assets/languages.yaml
index e4af70e..6a249db 100644
--- a/license-eye/assets/languages.yaml
+++ b/license-eye/assets/languages.yaml
@@ -2232,6 +2232,7 @@ Haskell:
   codemirror_mode: haskell
   codemirror_mime_type: text/x-haskell
   language_id: 157
+  comment_style_id: CurlyBracketDash
 Haxe:
   type: programming
   ace_mode: haxe
@@ -3708,6 +3709,7 @@ OCaml:
     - ocamlscript
   tm_scope: source.ocaml
   language_id: 255
+  comment_style_id: RoundBracketAsterisk
 ObjDump:
   type: data
   extensions:
@@ -3977,6 +3979,7 @@ PLSQL:
     - ".trg"
     - ".vw"
   language_id: 273
+  comment_style_id: DoubleDash
 PLpgSQL:
   type: programming
   ace_mode: pgsql
@@ -3987,6 +3990,7 @@ PLpgSQL:
     - ".pgsql"
     - ".sql"
   language_id: 274
+  comment_style_id: DoubleDash
 POV-Ray SDL:
   type: programming
   aliases:
@@ -5026,6 +5030,7 @@ SQL:
     - ".udf"
     - ".viw"
   language_id: 333
+  comment_style_id: DoubleDash
 SQLPL:
   type: programming
   ace_mode: sql
@@ -5036,6 +5041,7 @@ SQLPL:
     - ".sql"
     - ".db2"
   language_id: 334
+  comment_style_id: DoubleDash
 SRecode Template:
   type: markup
   color: "#348a34"
@@ -5549,6 +5555,7 @@ TSQL:
   ace_mode: sql
   tm_scope: source.tsql
   language_id: 918334941
+  comment_style_id: DoubleDash
 TSV:
   type: data
   ace_mode: text
@@ -5969,6 +5976,7 @@ Vim script:
     - vimrc
   ace_mode: text
   language_id: 388
+  comment_style_id: Quotes
 Visual Basic .NET:
   type: programming
   color: "#945db7"
diff --git a/license-eye/assets/styles.yaml b/license-eye/assets/styles.yaml
index deb64a5..216232f 100644
--- a/license-eye/assets/styles.yaml
+++ b/license-eye/assets/styles.yaml
@@ -18,16 +18,17 @@
 
 - id: DoubleSlash
   start: '//'
-  middle: ~
-  end: ~
+  middle: '//'
+  end: '//'
 
 - id: Hashtag
+  after: '(?m)^*#!.+$'
   start: '#'
-  middle: ~
-  end: ~
-  skip: '^#!'
+  middle: '#'
+  end: '#'
 
 - id: AngleBracket
+  after: '(?ms)^<\?.+?\?>$'
   start: '<!--'
   middle: '  ~'
   end: '-->'
@@ -38,3 +39,23 @@
   middle: ' *'          # <2>
   end: ' */'            # <3>
 # end::SlashAsterisk[]
+
+- id: RoundBracketAsterisk
+  start: '(*'
+  middle: '(*'
+  end: '(*'
+
+- id: CurlyBracketDash
+  start: '{-'
+  middle: ~
+  end: '-}'
+
+- id: DoubleDash
+  start: '--'
+  middle: '--'
+  end: '--'
+
+- id: Quotes
+  start: '"'
+  middle: '"'
+  end: '"'
diff --git a/license-eye/commands/header/check.go b/license-eye/commands/header/check.go
index 1de9115..c349ace 100644
--- a/license-eye/commands/header/check.go
+++ b/license-eye/commands/header/check.go
@@ -37,6 +37,11 @@ var CheckCommand = &cobra.Command{
 			return err
 		}
 
+		if len(args) > 0 {
+			logger.Log.Debugln("Overriding paths with command line args.")
+			config.Header.Paths = args
+		}
+
 		if err := header.Check(&config.Header, &result); err != nil {
 			return err
 		}
diff --git a/license-eye/internal/logger/log.go b/license-eye/internal/logger/log.go
index 7fc102e..1c641e8 100644
--- a/license-eye/internal/logger/log.go
+++ b/license-eye/internal/logger/log.go
@@ -29,7 +29,7 @@ func init() {
 	if Log == nil {
 		Log = logrus.New()
 	}
-	Log.Level = logrus.DebugLevel
+	Log.Level = logrus.InfoLevel
 	Log.SetOutput(os.Stdout)
 	Log.SetFormatter(&logrus.TextFormatter{
 		DisableTimestamp:       true,
diff --git a/license-eye/pkg/comments/config.go b/license-eye/pkg/comments/config.go
index 8b2db6f..28f4462 100644
--- a/license-eye/pkg/comments/config.go
+++ b/license-eye/pkg/comments/config.go
@@ -27,6 +27,7 @@ import (
 
 type CommentStyle struct {
 	ID     string `yaml:"id"`
+	After  string `yaml:"after"`
 	Start  string `yaml:"start"`
 	Middle string `yaml:"middle"`
 	End    string `yaml:"end"`
@@ -39,15 +40,6 @@ func (style *CommentStyle) Validate() error {
 	return nil
 }
 
-func (style *CommentStyle) Finalize() {
-	if style.Middle == "" {
-		style.Middle = style.Start
-	}
-	if style.End == "" {
-		style.End = style.Start
-	}
-}
-
 type Language struct {
 	Type           string   `yaml:"type"`
 	Extensions     []string `yaml:"extensions"`
@@ -107,7 +99,6 @@ func initCommentStyles() {
 	}
 
 	for _, style := range styles {
-		style.Finalize()
 		comments[style.ID] = style
 	}
 }
diff --git a/license-eye/pkg/config/Config.go b/license-eye/pkg/config/config.go
similarity index 100%
rename from license-eye/pkg/config/Config.go
rename to license-eye/pkg/config/config.go
diff --git a/license-eye/pkg/header/check.go b/license-eye/pkg/header/check.go
index a0541e6..af187a0 100644
--- a/license-eye/pkg/header/check.go
+++ b/license-eye/pkg/header/check.go
@@ -29,7 +29,7 @@ import (
 	"github.com/bmatcuk/doublestar/v2"
 )
 
-const CommentChars = "/*#- !~'\"(){}"
+var Punctuations = regexp.MustCompile("[\\[\\]/*:;\\s#\\-!~'\"(){}?]+") // TODO: also trim stop words
 
 // Check checks the license headers of the specified paths/globs.
 func Check(config *ConfigHeader, result *Result) error {
@@ -111,28 +111,29 @@ func CheckFile(file string, config *ConfigHeader, result *Result) error {
 	reader, err := os.Open(file)
 
 	if err != nil {
-		return nil
+		return err
 	}
 
 	var lines []string
 
 	scanner := bufio.NewScanner(reader)
 	for scanner.Scan() {
-		line := strings.ToLower(strings.Trim(scanner.Text(), CommentChars))
-		line = regexp.MustCompile("[ '\"]+").ReplaceAllString(line, " ")
+		line := strings.ToLower(Punctuations.ReplaceAllString(scanner.Text(), " "))
 		if len(line) > 0 {
 			lines = append(lines, line)
 		}
 	}
 
-	content := strings.Join(lines, " ")
+	content := Punctuations.ReplaceAllString(strings.Join(lines, " "), " ")
 	license, pattern := config.NormalizedLicense(), config.NormalizedPattern()
 
 	if strings.Contains(content, license) || (pattern != nil && pattern.MatchString(content)) {
 		result.Succeed(file)
 	} else {
 		logger.Log.Debugln("Content is:", content)
-		logger.Log.Debugln("Pattern is:", pattern)
+		if pattern != nil {
+			logger.Log.Debugln("Pattern is:", pattern)
+		}
 
 		result.Fail(file)
 	}
diff --git a/license-eye/pkg/header/check_test.go b/license-eye/pkg/header/check_test.go
new file mode 100644
index 0000000..2cc2de7
--- /dev/null
+++ b/license-eye/pkg/header/check_test.go
@@ -0,0 +1,143 @@
+//
+// 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 header
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+	"testing"
+
+	"gopkg.in/yaml.v3"
+)
+
+var c struct {
+	Header ConfigHeader `yaml:"header"`
+}
+
+func init() {
+	content, err := ioutil.ReadFile("../../test/testdata/.licenserc_for_test_check.yaml")
+	if err != nil {
+		panic(err)
+	}
+	if err := yaml.Unmarshal(content, &c); err != nil {
+		panic(err)
+	}
+	if err := c.Header.Finalize(); err != nil {
+		panic(err)
+	}
+}
+
+func TestCheckFile(t *testing.T) {
+	type args struct {
+		name       string
+		file       string
+		result     *Result
+		wantErr    bool
+		hasFailure bool
+	}
+	tests := func() []args {
+		files, err := filepath.Glob("../../test/testdata/include_test/with_license/*")
+		if err != nil {
+			t.Error(err)
+		}
+
+		var cases []args
+
+		for _, file := range files {
+			cases = append(cases, args{
+				name:       file,
+				file:       file,
+				result:     &Result{},
+				wantErr:    false,
+				hasFailure: false,
+			})
+		}
+
+		return cases
+	}()
+
+	if len(tests) == 0 {
+		t.Errorf("Tests should not be empty")
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if strings.TrimSpace(c.Header.License) == "" {
+				t.Errorf("License should not be empty")
+			}
+			if err := CheckFile(tt.file, &c.Header, tt.result); (err != nil) != tt.wantErr {
+				t.Errorf("CheckFile() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			if len(tt.result.Ignored) > 0 {
+				t.Errorf("Should not ignore any file, %v", tt.result.Ignored)
+			}
+			if tt.result.HasFailure() != tt.hasFailure {
+				t.Errorf("CheckFile() result has failure = %v, wanted = %v", tt.result.Failure, tt.hasFailure)
+			}
+		})
+	}
+}
+
+func TestCheckFileFailure(t *testing.T) {
+	type args struct {
+		name       string
+		file       string
+		result     *Result
+		wantErr    bool
+		hasFailure bool
+	}
+	tests := func() []args {
+		files, err := filepath.Glob("../../test/testdata/include_test/without_license/*")
+		if err != nil {
+			panic(err)
+		}
+
+		var cases []args
+
+		for _, file := range files {
+			cases = append(cases, args{
+				name:       file,
+				file:       file,
+				result:     &Result{},
+				wantErr:    false,
+				hasFailure: true,
+			})
+		}
+
+		return cases
+	}()
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if strings.TrimSpace(c.Header.License) == "" {
+				t.Errorf("License should not be empty")
+			}
+			if err := CheckFile(tt.file, &c.Header, tt.result); (err != nil) != tt.wantErr {
+				t.Errorf("CheckFile() error = %v, wantErr %v", err, tt.wantErr)
+			}
+			if len(tt.result.Ignored) > 0 {
+				t.Errorf("Should not ignore any file, %v", tt.result.Ignored)
+			}
+			if tt.result.HasFailure() != tt.hasFailure {
+				t.Errorf("CheckFile() result has failure = %v, wanted = %v", tt.result.Failure, tt.hasFailure)
+			}
+		})
+	}
+}
diff --git a/license-eye/pkg/header/config.go b/license-eye/pkg/header/config.go
index 0293d1f..82f4361 100644
--- a/license-eye/pkg/header/config.go
+++ b/license-eye/pkg/header/config.go
@@ -36,17 +36,15 @@ type ConfigHeader struct {
 }
 
 // NormalizedLicense returns the normalized string of the license content,
-// "normalized" means the linebreaks and CommentChars are all trimmed.
+// "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 {
-			line = strings.ToLower(strings.Trim(line, CommentChars))
-			line = regexp.MustCompile(" +").ReplaceAllString(line, " ")
-			lines = append(lines, line)
+			lines = append(lines, Punctuations.ReplaceAllString(line, " "))
 		}
 	}
-	return strings.Join(lines, " ")
+	return strings.ToLower(regexp.MustCompile("(?m)[\\s\"']+").ReplaceAllString(strings.Join(lines, " "), " "))
 }
 
 func (config *ConfigHeader) NormalizedPattern() *regexp.Regexp {
@@ -57,11 +55,11 @@ func (config *ConfigHeader) NormalizedPattern() *regexp.Regexp {
 	var lines []string
 	for _, line := range strings.Split(config.Pattern, "\n") {
 		if len(line) > 0 {
-			line = regexp.MustCompile("[ \"']+").ReplaceAllString(line, " ")
-			lines = append(lines, strings.TrimSpace(line))
+			lines = append(lines, line)
 		}
 	}
-	return regexp.MustCompile("(?i).*" + strings.Join(lines, " ") + ".*")
+	content := regexp.MustCompile("(?m)[\\s\"':;/\\-]+").ReplaceAllString(strings.Join(lines, " "), " ")
+	return regexp.MustCompile("(?i).*" + content + ".*")
 }
 
 // Parse reads and parses the header check configurations in config file.
diff --git a/license-eye/pkg/header/fix.go b/license-eye/pkg/header/fix.go
index a710106..40acde7 100644
--- a/license-eye/pkg/header/fix.go
+++ b/license-eye/pkg/header/fix.go
@@ -22,6 +22,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"regexp"
 	"strings"
 
 	"github.com/apache/skywalking-eyes/license-eye/internal/logger"
@@ -60,12 +61,14 @@ func InsertComment(file string, style *comments.CommentStyle, config *ConfigHead
 		return err
 	}
 
-	lines, err := generateLicenseHeader(style, config)
+	licenseHeader, err := generateLicenseHeader(style, config)
 	if err != nil {
 		return err
 	}
 
-	if err := ioutil.WriteFile(file, append([]byte(lines), content...), stat.Mode()); err != nil {
+	content = rewriteContent(style, content, licenseHeader)
+
+	if err := ioutil.WriteFile(file, content, stat.Mode()); err != nil {
 		return err
 	}
 
@@ -74,7 +77,22 @@ func InsertComment(file string, style *comments.CommentStyle, config *ConfigHead
 	return nil
 }
 
-// TODO: tackle with shebang and xml declaration
+func rewriteContent(style *comments.CommentStyle, content []byte, licenseHeader string) []byte {
+	if style.After == "" {
+		return append([]byte(licenseHeader), content...)
+	}
+
+	content = []byte(strings.TrimLeft(string(content), " \n"))
+	afterPattern := regexp.MustCompile(style.After)
+	location := afterPattern.FindIndex(content)
+	if location == nil || len(location) != 2 {
+		return append([]byte(licenseHeader), content...)
+	}
+	return append(content[0:location[1]],
+		append(append([]byte("\n"), []byte(licenseHeader)...), content[location[1]+1:]...)...,
+	)
+}
+
 func generateLicenseHeader(style *comments.CommentStyle, config *ConfigHeader) (string, error) {
 	if err := style.Validate(); err != nil {
 		return "", err
@@ -82,7 +100,7 @@ func generateLicenseHeader(style *comments.CommentStyle, config *ConfigHeader) (
 
 	middleLines := strings.Split(config.License, "\n")
 	for i, line := range middleLines {
-		middleLines[i] = fmt.Sprintf("%v %v", style.Middle, line)
+		middleLines[i] = strings.TrimRight(fmt.Sprintf("%v %v", style.Middle, line), " ")
 	}
 
 	lines := fmt.Sprintf("%v\n%v\n", style.Start, strings.Join(middleLines, "\n"))
@@ -90,5 +108,5 @@ func generateLicenseHeader(style *comments.CommentStyle, config *ConfigHeader) (
 		lines += style.End
 	}
 
-	return lines, nil
+	return strings.TrimSpace(lines) + "\n", nil
 }
diff --git a/license-eye/pkg/header/fix_test.go b/license-eye/pkg/header/fix_test.go
index a3ea449..c6f307f 100644
--- a/license-eye/pkg/header/fix_test.go
+++ b/license-eye/pkg/header/fix_test.go
@@ -1,85 +1,63 @@
+//
+// 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 header
 
 import (
+	"reflect"
 	"testing"
 
 	"github.com/apache/skywalking-eyes/license-eye/pkg/comments"
 )
 
 var config = &ConfigHeader{
-	License: `
-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.
-`,
+	License: `Apache License 2.0
+  http://www.apache.org/licenses/LICENSE-2.0
+Apache License 2.0`,
 }
 
-func Test(t *testing.T) {
+func TestFix(t *testing.T) {
 	tests := []struct {
 		filename string
 		comments string
 	}{
 		{
 			filename: "Test.java",
-			comments: `/* 
- * 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.
- * 
+			comments: `/*
+ * Apache License 2.0
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * Apache License 2.0
  */
 `,
 		},
 		{
 			filename: "Test.py",
 			comments: `#
-# 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.
-# 
-#
+# Apache License 2.0
+#   http://www.apache.org/licenses/LICENSE-2.0
+# Apache License 2.0
 `,
 		},
 	}
 	for _, test := range tests {
 		t.Run(test.filename, func(t *testing.T) {
 			style := comments.FileCommentStyle(test.filename)
-			if c, err := generateLicenseHeader(style, config); err != nil && c != test.comments {
+			if c, err := generateLicenseHeader(style, config); err != nil || c != test.comments {
 				t.Log("Actual:", c)
 				t.Log("Expected:", test.comments)
 				t.Logf("Middle:'%v'\n", style.Middle)
@@ -89,3 +67,155 @@ func Test(t *testing.T) {
 		})
 	}
 }
+
+func TestRewriteContent(t *testing.T) {
+	tests := []struct {
+		name            string
+		style           *comments.CommentStyle
+		content         string
+		licenseHeader   string
+		expectedContent string
+	}{
+		{
+			name:  "Ocaml",
+			style: comments.FileCommentStyle("test.ml"),
+			content: `print_string "hello worlds!\n";;
+`,
+			licenseHeader: getLicenseHeader("test.ml", t.Error),
+			expectedContent: `(*
+(* Apache License 2.0
+(*   http://www.apache.org/licenses/LICENSE-2.0
+(* Apache License 2.0
+print_string "hello worlds!\n";;
+`},
+		{
+			name:  "Python with Shebang",
+			style: comments.FileCommentStyle("test.py"),
+			content: `
+#!/usr/bin/env python3
+if __name__ == '__main__':
+    print('Hello World')
+`,
+			licenseHeader: getLicenseHeader("test.py", t.Error),
+			expectedContent: `#!/usr/bin/env python3
+#
+# Apache License 2.0
+#   http://www.apache.org/licenses/LICENSE-2.0
+# Apache License 2.0
+if __name__ == '__main__':
+    print('Hello World')
+`},
+		{
+			name:  "Python",
+			style: comments.FileCommentStyle("test.py"),
+			content: `
+if __name__ == '__main__':
+    print('Hello World')
+`,
+			licenseHeader: getLicenseHeader("test.py", t.Error),
+			expectedContent: `#
+# Apache License 2.0
+#   http://www.apache.org/licenses/LICENSE-2.0
+# Apache License 2.0
+if __name__ == '__main__':
+    print('Hello World')
+`},
+		{
+			name:  "XML one line declaration",
+			style: comments.FileCommentStyle("test.xml"),
+			content: `
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+  <modelVersion>4.0.0</modelVersion>
+</project>
+`,
+			licenseHeader: getLicenseHeader("test.xml", t.Error),
+			expectedContent: `<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Apache License 2.0
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~ Apache License 2.0
+-->
+<project>
+  <modelVersion>4.0.0</modelVersion>
+</project>
+`},
+		{
+			name:  "XML multi-line declaration",
+			style: comments.FileCommentStyle("test.xml"),
+			content: `
+<?xml
+  version="1.0"
+  encoding="UTF-8"
+?>
+<project>
+  <modelVersion>4.0.0</modelVersion>
+</project>
+`,
+			licenseHeader: getLicenseHeader("test.xml", t.Error),
+			expectedContent: `<?xml
+  version="1.0"
+  encoding="UTF-8"
+?>
+<!--
+  ~ Apache License 2.0
+  ~   http://www.apache.org/licenses/LICENSE-2.0
+  ~ Apache License 2.0
+-->
+<project>
+  <modelVersion>4.0.0</modelVersion>
+</project>
+`},
+		{
+			name:          "SQL",
+			style:         comments.FileCommentStyle("test.sql"),
+			content:       `select * from user;`,
+			licenseHeader: getLicenseHeader("test.sql", t.Error),
+			expectedContent: `--
+-- Apache License 2.0
+--   http://www.apache.org/licenses/LICENSE-2.0
+-- Apache License 2.0
+select * from user;`},
+		{
+			name:          "Haskell",
+			style:         comments.FileCommentStyle("test.hs"),
+			content:       `import Foundation.Hashing.Hashable`,
+			licenseHeader: getLicenseHeader("test.hs", t.Error),
+			expectedContent: `{-
+ Apache License 2.0
+   http://www.apache.org/licenses/LICENSE-2.0
+ Apache License 2.0
+-}
+import Foundation.Hashing.Hashable`},
+		{
+			name:  "Vim",
+			style: comments.FileCommentStyle("test.vim"),
+			content: `echo 'Hello' | echo 'world!'
+`,
+			licenseHeader: getLicenseHeader("test.vim", t.Error),
+			expectedContent: `"
+" Apache License 2.0
+"   http://www.apache.org/licenses/LICENSE-2.0
+" Apache License 2.0
+echo 'Hello' | echo 'world!'
+`},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			content := rewriteContent(test.style, []byte(test.content), test.licenseHeader)
+			if !reflect.DeepEqual(content, []byte(test.expectedContent)) {
+				t.Log("Actual\n" + string(content))
+				t.Log("Expected\n" + test.expectedContent)
+				t.Fail()
+			}
+		})
+	}
+}
+
+func getLicenseHeader(filename string, tError func(args ...interface{})) string {
+	s, err := generateLicenseHeader(comments.FileCommentStyle(filename), config)
+	if err != nil {
+		tError(err)
+	}
+	return s
+}
diff --git a/license-eye/test/testdata/.licenserc_for_test_check.yaml b/license-eye/test/testdata/.licenserc_for_test_check.yaml
index 1ae329e..eee7d39 100644
--- a/license-eye/test/testdata/.licenserc_for_test_check.yaml
+++ b/license-eye/test/testdata/.licenserc_for_test_check.yaml
@@ -18,7 +18,7 @@ header:
     under the License.
 
   paths:
-    - 'testdata/**'
+    - 'test/testdata/**'
 
   paths-ignore:
     - '**/.DS_Store'
diff --git a/license-eye/test/testdata/.licenserc_for_test_fix.yaml b/license-eye/test/testdata/.licenserc_for_test_fix.yaml
index 008e9f4..facb12f 100644
--- a/license-eye/test/testdata/.licenserc_for_test_fix.yaml
+++ b/license-eye/test/testdata/.licenserc_for_test_fix.yaml
@@ -18,7 +18,7 @@ header:
     under the License.
 
   paths:
-    - 'testdata/include_test/**'
+    - 'test/testdata/include_test/**'
 
   paths-ignore:
     - '**/.DS_Store'
diff --git a/license-eye/test/testdata/include_test/with_license/testcase.go b/license-eye/test/testdata/include_test/with_license/testcase.go
index c382b60..9b47627 100644
--- a/license-eye/test/testdata/include_test/with_license/testcase.go
+++ b/license-eye/test/testdata/include_test/with_license/testcase.go
@@ -2,7 +2,8 @@
 // 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
+// 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/test/testdata/include_test/with_license/testcase.java b/license-eye/test/testdata/include_test/with_license/testcase.java
index a388cbc..c24ce1f 100644
--- a/license-eye/test/testdata/include_test/with_license/testcase.java
+++ b/license-eye/test/testdata/include_test/with_license/testcase.java
@@ -1,9 +1,12 @@
 /**
- * Licensed to Apache Software Foundation (ASF) under one or more contributor
+ * 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
+ * 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
  *
@@ -12,7 +15,7 @@
  * 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
+ * KIND, either express or implied.       See the License for the
  * specific language governing permissions and limitations
  * under the License.
  */
diff --git a/license-eye/test/testdata/include_test/with_license/testcase.ml b/license-eye/test/testdata/include_test/with_license/testcase.ml
new file mode 100644
index 0000000..246ff88
--- /dev/null
+++ b/license-eye/test/testdata/include_test/with_license/testcase.ml
@@ -0,0 +1,19 @@
+(* 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.
+
+let to_string = function Left -> "Left" | Non -> "Non" | Right -> "Right"
diff --git a/license-eye/test/testdata/include_test/without_license/testcase.py b/license-eye/test/testdata/include_test/without_license/testcase.py
index 5030e1c..ce55ff9 100644
--- a/license-eye/test/testdata/include_test/without_license/testcase.py
+++ b/license-eye/test/testdata/include_test/without_license/testcase.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 # Esse enim dolore adipisicing in cillum eiusmod excepteur quis nisi sit dolor anim anim id id nostrud nostrud tempor.
 # Elit sit enim cillum adipisicing non magna aute nostrud ullamco dolor dolore consequat ut ea occaecat veniam incididunt
 #  occaecat consectetur eiusmod sint eiusmod aute eu duis fugiat dolore in laboris enim eiusmod aliquip nisi aliqua irure 


[skywalking-eyes] 02/02: Append .gitignore content automatically, add `LicenseLocationThreshold`

Posted by ke...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch enhance
in repository https://gitbox.apache.org/repos/asf/skywalking-eyes.git

commit b3725378f56a947cc84f22f96a6590064cf12064
Author: kezhenxu94 <ke...@apache.org>
AuthorDate: Tue Dec 22 19:39:01 2020 +0800

    Append .gitignore content automatically, add `LicenseLocationThreshold`
---
 .licenserc.yaml                   | 32 +++++++++++++++++++++++++-------
 license-eye/assets/languages.yaml |  1 +
 license-eye/pkg/header/check.go   | 23 +++++++++++++++++++++--
 license-eye/pkg/header/config.go  | 27 +++++++++++++++++++++++++++
 4 files changed, 74 insertions(+), 9 deletions(-)

diff --git a/.licenserc.yaml b/.licenserc.yaml
index 049ce1b..a6ab99c 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -1,3 +1,21 @@
+#
+# 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.
+#
 header: # `header` section is configurations for source codes license header.
   license: | # `license` will be used as the content when `fix` command needs to insert a license header.
     Licensed to Apache Software Foundation (ASF) under one or more contributor
@@ -16,7 +34,10 @@ header: # `header` section is configurations for source codes license header.
     KIND, either express or implied.  See the License for the
     specific language governing permissions and limitations
     under the License.
-  pattern: | # `pattern` is optional regexp if all the file headers are the same as `license` (linebreaks doesn't matter).
+
+  # `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
     license agreements. See the NOTICE file distributed with
     this work for additional information regarding copyright
@@ -34,14 +55,12 @@ header: # `header` section is configurations for source codes license header.
     specific language governing permissions and limitations
     under the License.
 
-  paths: # `paths` are the path list that will be checked (and fixed) by license-eye.
+  paths: # `paths` are the path list that will be checked (and fixed) by license-eye, default is ['**'].
     - '**'
 
   paths-ignore: # `paths-ignore` are the path list that will be ignored by license-eye.
-    - '.git/**'
-    - '.idea/**'
-    - 'dist/**'
-    - 'licenses/**'
+    - 'dist'
+    - 'licenses'
     - '**/bin/**'
     - '**/*.md'
     - '**/.DS_Store'
@@ -50,5 +69,4 @@ header: # `header` section is configurations for source codes license header.
     - '**/go.sum'
     - 'LICENSE'
     - 'NOTICE'
-    - '**/assets.gen.go'
     - '**/assets/languages.yaml'
diff --git a/license-eye/assets/languages.yaml b/license-eye/assets/languages.yaml
index 6a249db..2d179ae 100644
--- a/license-eye/assets/languages.yaml
+++ b/license-eye/assets/languages.yaml
@@ -6401,6 +6401,7 @@ YAML:
   codemirror_mode: yaml
   codemirror_mime_type: text/x-yaml
   language_id: 407
+  comment_style_id: Hashtag
 YANG:
   type: data
   extensions:
diff --git a/license-eye/pkg/header/check.go b/license-eye/pkg/header/check.go
index af187a0..45db2df 100644
--- a/license-eye/pkg/header/check.go
+++ b/license-eye/pkg/header/check.go
@@ -29,7 +29,13 @@ import (
 	"github.com/bmatcuk/doublestar/v2"
 )
 
-var Punctuations = regexp.MustCompile("[\\[\\]/*:;\\s#\\-!~'\"(){}?]+") // TODO: also trim stop words
+// 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 {
@@ -127,7 +133,7 @@ func CheckFile(file string, config *ConfigHeader, result *Result) error {
 	content := Punctuations.ReplaceAllString(strings.Join(lines, " "), " ")
 	license, pattern := config.NormalizedLicense(), config.NormalizedPattern()
 
-	if strings.Contains(content, license) || (pattern != nil && pattern.MatchString(content)) {
+	if satisfy(content, license, pattern) {
 		result.Succeed(file)
 	} else {
 		logger.Log.Debugln("Content is:", content)
@@ -140,3 +146,16 @@ func CheckFile(file string, config *ConfigHeader, result *Result) error {
 
 	return nil
 }
+
+func satisfy(content string, license string, pattern *regexp.Regexp) bool {
+	if index := strings.Index(content, license); index >= 0 {
+		return index < LicenseLocationThreshold
+	}
+
+	if pattern == nil {
+		return false
+	}
+	index := pattern.FindStringIndex(content)
+
+	return index != nil && len(index) == 2 && index[0] < LicenseLocationThreshold
+}
diff --git a/license-eye/pkg/header/config.go b/license-eye/pkg/header/config.go
index 82f4361..8911cfc 100644
--- a/license-eye/pkg/header/config.go
+++ b/license-eye/pkg/header/config.go
@@ -18,7 +18,9 @@
 package header
 
 import (
+	"bufio"
 	"io/ioutil"
+	"os"
 	"regexp"
 	"strings"
 
@@ -87,6 +89,16 @@ func (config *ConfigHeader) ShouldIgnore(path string) (bool, error) {
 			return matched, err
 		}
 	}
+
+	if stat, err := os.Stat(path); err == nil {
+		for _, ignorePattern := range config.PathsIgnore {
+			ignorePattern = strings.TrimRight(ignorePattern, "/")
+			if strings.HasPrefix(path, ignorePattern+"/") || stat.Name() == ignorePattern {
+				return true, nil
+			}
+		}
+	}
+
 	return false, nil
 }
 
@@ -97,5 +109,20 @@ func (config *ConfigHeader) Finalize() error {
 		config.Paths = []string{"**"}
 	}
 
+	config.PathsIgnore = append(config.PathsIgnore, ".git")
+
+	if file, err := os.Open(".gitignore"); err == nil {
+		defer func() { _ = file.Close() }()
+
+		for scanner := bufio.NewScanner(file); scanner.Scan(); {
+			line := scanner.Text()
+			if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" {
+				continue
+			}
+			logger.Log.Debugln("Add ignore path from .gitignore:", line)
+			config.PathsIgnore = append(config.PathsIgnore, strings.TrimSpace(line))
+		}
+	}
+
 	return nil
 }