-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Evsyukov Denis <denis.evsyukov@flant.com>
- Loading branch information
Showing
5 changed files
with
170 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,128 +1,93 @@ | ||
package copyright | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
var EELicenseRe = regexp.MustCompile(`(?s)Copyright 202[1-9] Flant JSC.*Licensed under the Deckhouse Platform Enterprise Edition \(EE\) license.*See https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE`) | ||
"github.com/deckhouse/d8-lint/pkg/config" | ||
"github.com/deckhouse/d8-lint/pkg/errors" | ||
"github.com/deckhouse/d8-lint/pkg/module" | ||
) | ||
|
||
var CELicenseRe = regexp.MustCompile(`(?s)[/#{!-]*(\s)*Copyright 202[1-9] Flant JSC[-!}\n#/]* | ||
[/#{!-]*(\s)*Licensed under the Apache License, Version 2.0 \(the \"License\"\);[-!}\n]* | ||
[/#{!-]*(\s)*you may not use this file except in compliance with the License.[-!}\n]* | ||
[/#{!-]*(\s)*You may obtain a copy of the License at[-!}\n#/]* | ||
[/#{!-]*(\s)*http://www.apache.org/licenses/LICENSE-2.0[-!}\n#/]* | ||
[/#{!-]*(\s)*Unless required by applicable law or agreed to in writing, software[-!}\n]* | ||
[/#{!-]*(\s)*distributed under the License is distributed on an \"AS IS\" BASIS,[-!}\n]* | ||
[/#{!-]*(\s)*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.[-!}\n]* | ||
[/#{!-]*(\s)*See the License for the specific language governing permissions and[-!}\n]* | ||
[/#{!-]*(\s)*limitations under the License.[-!}\n]*`) | ||
// Copyright linter | ||
type Copyright struct { | ||
name, desc string | ||
cfg *config.CopyrightSettings | ||
} | ||
|
||
var fileToCheckRe = regexp.MustCompile(`\.go$|/[^/.]+$|\.sh$|\.lua$|\.py$|^\.github/(scripts|workflows|workflow_templates)/.+\.(js|yml|yaml|sh)$`) | ||
var fileToSkipRe = regexp.MustCompile(`geohash.lua$|\.github/CODEOWNERS|Dockerfile$|Makefile$|/docs/documentation/|/docs/site/|bashrc$|inputrc$|modules_menu_skip$|LICENSE$|tools/spelling/.+`) | ||
type Module interface { | ||
GetName() string | ||
GetPath() string | ||
} | ||
|
||
func RunCopyrightValidation(info *DiffInfo) (exitCode int) { | ||
fmt.Printf("Run 'copyright' validation ...\n") | ||
func New(cfg *config.CopyrightSettings) *Copyright { | ||
return &Copyright{ | ||
name: "copyright", | ||
desc: "Copyright will check all files in the modules for contains copyright", | ||
cfg: cfg, | ||
} | ||
} | ||
|
||
if len(info.Files) == 0 { | ||
fmt.Printf("Nothing to validate, diff is empty\n") | ||
os.Exit(0) | ||
func (o *Copyright) Run(m *module.Module) (errors.LintRuleErrorsList, error) { | ||
files, err := o.getFiles(m.GetPath()) | ||
if err != nil { | ||
return errors.LintRuleErrorsList{}, err | ||
} | ||
|
||
exitCode = 0 | ||
msgs := NewMessages() | ||
for _, fileInfo := range info.Files { | ||
if !fileInfo.HasContent() { | ||
var result errors.LintRuleErrorsList | ||
for _, fileName := range files { | ||
name, _ := strings.CutPrefix(fileName, m.GetPath()) | ||
name = m.GetName() + ":" + name | ||
if _, ok := o.cfg.CopyrightExcludes[name]; ok { | ||
continue | ||
} | ||
|
||
fileName := fileInfo.NewFileName | ||
|
||
if fileToCheckRe.MatchString(fileName) && !fileToSkipRe.MatchString(fileName) { | ||
msgs.Add(checkFileCopyright(fileName)) | ||
} else { | ||
msgs.Add(NewSkip(fileName, "")) | ||
ok, er := checkFileCopyright(fileName) | ||
if !ok { | ||
path, _ := strings.CutPrefix(fileName, m.GetPath()) | ||
result.Add(errors.NewLintRuleError( | ||
"copyright", | ||
path, | ||
er, | ||
"errors in `%s` module", | ||
m.GetName(), | ||
)) | ||
} | ||
} | ||
msgs.PrintReport() | ||
|
||
if msgs.CountErrors() > 0 { | ||
exitCode = 1 | ||
} | ||
|
||
return exitCode | ||
return result, nil | ||
} | ||
|
||
var copyrightOrAutogenRe = regexp.MustCompile(`Copyright The|autogenerated|DO NOT EDIT`) | ||
var copyrightRe = regexp.MustCompile(`Copyright`) | ||
var flantRe = regexp.MustCompile(`Flant|Deckhouse`) | ||
func (o *Copyright) getFiles(rootPath string) ([]string, error) { | ||
var result []string | ||
err := filepath.Walk(rootPath, func(path string, info os.FileInfo, _ error) error { | ||
if info.Mode()&os.ModeSymlink != 0 { | ||
return filepath.SkipDir | ||
} | ||
|
||
// checkFileCopyright returns true if file is readable and has no copyright information in it. | ||
func checkFileCopyright(fName string) Message { | ||
// Original script 'validate_copyright.sh' used 'head -n 10'. | ||
// Here we just read first 1024 bytes. | ||
headBuf, err := readFileHead(fName, 1024) | ||
if err != nil { | ||
return NewSkip(fName, err.Error()) | ||
} | ||
if info.IsDir() { | ||
if info.Name() == ".git" { | ||
return filepath.SkipDir | ||
} | ||
|
||
// Skip autogenerated file or file already has other than Flant copyright | ||
if copyrightOrAutogenRe.Match(headBuf) { | ||
return NewSkip(fName, "generated code or other license") | ||
} | ||
return nil | ||
} | ||
|
||
// Check Flant license if file contains keywords. | ||
if flantRe.Match(headBuf) { | ||
return checkFlantLicense(fName, headBuf) | ||
} | ||
if fileToCheckRe.MatchString(path) && !fileToSkipRe.MatchString(path) { | ||
result = append(result, path) | ||
} | ||
|
||
// Skip file with some other copyright | ||
if copyrightRe.Match(headBuf) { | ||
return NewSkip(fName, "contains other license") | ||
} | ||
return nil | ||
}) | ||
|
||
return NewError(fName, "no copyright or license information", "") | ||
return result, err | ||
} | ||
|
||
func checkFlantLicense(fName string, buf []byte) Message { | ||
if strings.HasPrefix(fName, "/ee/") || strings.HasPrefix(fName, "ee/") { | ||
if !EELicenseRe.Match(buf) { | ||
return NewError(fName, "EE related file should contain EE license", "") | ||
} | ||
} else { | ||
if !CELicenseRe.Match(buf) { | ||
return NewError(fName, "should contain CE license", "") | ||
} | ||
} | ||
|
||
return NewOK(fName) | ||
func (o *Copyright) Name() string { | ||
return o.name | ||
} | ||
|
||
func readFileHead(fName string, size int) ([]byte, error) { | ||
file, err := os.Open(fName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer file.Close() | ||
|
||
fi, err := file.Stat() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if fi.IsDir() { | ||
return nil, fmt.Errorf("directory") | ||
} | ||
if fi.Mode()&os.ModeSymlink != 0 { | ||
return nil, fmt.Errorf("symlink") | ||
} | ||
|
||
headBuf := make([]byte, size) | ||
_, err = file.Read(headBuf) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return headBuf, nil | ||
func (o *Copyright) Desc() string { | ||
return o.desc | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package copyright | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var EELicenseRe = regexp.MustCompile(`(?s)Copyright 202[1-9] Flant JSC.*Licensed under the Deckhouse Platform Enterprise Edition \(EE\) license.*See https://github.com/deckhouse/deckhouse/blob/main/ee/LICENSE`) | ||
|
||
var CELicenseRe = regexp.MustCompile(`(?s)[/#{!-]*(\s)*Copyright 202[1-9] Flant JSC[-!}\n#/]* | ||
[/#{!-]*(\s)*Licensed under the Apache License, Version 2.0 \(the "License"\);[-!}\n]* | ||
[/#{!-]*(\s)*you may not use this file except in compliance with the License.[-!}\n]* | ||
[/#{!-]*(\s)*You may obtain a copy of the License at[-!}\n#/]* | ||
[/#{!-]*(\s)*http://www.apache.org/licenses/LICENSE-2.0[-!}\n#/]* | ||
[/#{!-]*(\s)*Unless required by applicable law or agreed to in writing, software[-!}\n]* | ||
[/#{!-]*(\s)*distributed under the License is distributed on an "AS IS" BASIS,[-!}\n]* | ||
[/#{!-]*(\s)*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.[-!}\n]* | ||
[/#{!-]*(\s)*See the License for the specific language governing permissions and[-!}\n]* | ||
[/#{!-]*(\s)*limitations under the License.[-!}\n]*`) | ||
|
||
var fileToCheckRe = regexp.MustCompile(`\.go$|/[^/.]+$|\.sh$|\.lua$|\.py$|^\.github/(scripts|workflows|workflow_templates)/.+\.(js|yml|yaml|sh)$`) | ||
var fileToSkipRe = regexp.MustCompile(`geohash.lua$|\.github/CODEOWNERS|Dockerfile$|Makefile$|/docs/documentation/|/docs/site/|bashrc$|inputrc$|modules_menu_skip$|LICENSE$|tools/spelling/.+`) | ||
|
||
var copyrightOrAutogenRe = regexp.MustCompile(`Copyright The|autogenerated|DO NOT EDIT`) | ||
var copyrightRe = regexp.MustCompile(`Copyright`) | ||
var flantRe = regexp.MustCompile(`Flant|Deckhouse`) | ||
|
||
// checkFileCopyright returns true if file is readable and has no copyright information in it. | ||
func checkFileCopyright(fName string) (bool, error) { | ||
// Original script 'validate_copyright.sh' used 'head -n 10'. | ||
// Here we just read first 1024 bytes. | ||
headBuf, err := readFileHead(fName, 1024) | ||
if err != nil { | ||
return false, err | ||
} | ||
|
||
// Skip autogenerated file or file already has other than Flant copyright | ||
if copyrightOrAutogenRe.Match(headBuf) { | ||
return true, errors.New("generated code or other license") | ||
} | ||
|
||
// Check Flant license if file contains keywords. | ||
if flantRe.Match(headBuf) { | ||
return true, nil | ||
} | ||
|
||
// Skip file with some other copyright | ||
if copyrightRe.Match(headBuf) { | ||
return true, errors.New("contains other license") | ||
} | ||
|
||
return false, errors.New("no copyright or license information") | ||
} | ||
|
||
func checkFlantLicense(fName string, buf []byte) error { | ||
if strings.HasPrefix(fName, "/ee/") || strings.HasPrefix(fName, "ee/") { | ||
if !EELicenseRe.Match(buf) { | ||
return errors.New("EE related file should contain EE license") | ||
} | ||
} else { | ||
if !CELicenseRe.Match(buf) { | ||
return errors.New("should contain CE license") | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func readFileHead(fName string, size int) ([]byte, error) { | ||
file, err := os.Open(fName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer file.Close() | ||
|
||
fi, err := file.Stat() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if fi.IsDir() { | ||
return nil, fmt.Errorf("directory") | ||
} | ||
if fi.Mode()&os.ModeSymlink != 0 { | ||
return nil, fmt.Errorf("symlink") | ||
} | ||
|
||
headBuf := make([]byte, size) | ||
_, err = file.Read(headBuf) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return headBuf, nil | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters