diff --git a/.golangci.yml b/.golangci.yml index a52f62a7..9cd559d7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -68,6 +68,7 @@ linters: - all - '-ST1003' # waiting for package name will be fixed (underscores) - '-QF1008' # not need to fix; we understand how to call nested structs + - '-SA1019' # remove when fix logger revive: rules: - name: var-naming # waiting for package name will be fixed (incorrect naming) diff --git a/internal/logger/logger.go b/internal/logger/logger.go index edc329d8..1fa40e28 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -52,21 +52,25 @@ func InitLogger(w io.Writer, logLevel string) { log.SetOutput(io.Discard) } +// Deprecated: use slog directly instead of these wrapper functions func DebugF(format string, a ...any) { slog.Debug( fmt.Sprintf(format, a...)) } +// Deprecated: use slog directly instead of these wrapper functions func InfoF(format string, a ...any) { slog.Info( fmt.Sprintf(format, a...)) } +// Deprecated: use slog directly instead of these wrapper functions func WarnF(format string, a ...any) { slog.Warn( fmt.Sprintf(format, a...)) } +// Deprecated: use slog directly instead of these wrapper functions func ErrorF(format string, a ...any) { slog.Error( fmt.Sprintf(format, a...)) diff --git a/pkg/linters/module/rules/license.go b/pkg/linters/module/rules/license.go index 591ae3aa..56e48daf 100644 --- a/pkg/linters/module/rules/license.go +++ b/pkg/linters/module/rules/license.go @@ -17,6 +17,7 @@ limitations under the License. package rules import ( + "errors" "os" "regexp" @@ -24,7 +25,7 @@ import ( "github.com/deckhouse/dmt/internal/logger" "github.com/deckhouse/dmt/internal/module" "github.com/deckhouse/dmt/pkg" - "github.com/deckhouse/dmt/pkg/errors" + pkgerrors "github.com/deckhouse/dmt/pkg/errors" ) const ( @@ -59,7 +60,7 @@ type LicenseRule struct { pkg.PathRule } -func (r *LicenseRule) CheckFiles(mod *module.Module, errorList *errors.LintRuleErrorsList) { +func (r *LicenseRule) CheckFiles(mod *module.Module, errorList *pkgerrors.LintRuleErrorsList) { errorList = errorList.WithRule(r.GetName()) // Use new parser if available @@ -74,15 +75,15 @@ func (r *LicenseRule) CheckFiles(mod *module.Module, errorList *errors.LintRuleE continue } - licenseInfo, parseErr := parser.ParseFile(fileName) - if parseErr != nil { - errorList.WithFilePath(name).Error(parseErr.Error()) + _, parseErr := parser.ParseFile(fileName) + if errors.Is(parseErr, ErrUnsupportedFileType) { + logger.DebugF("Skipping unsupported file type: %s", name) continue } - // Handle parsed license info - if !licenseInfo.Valid { - errorList.WithFilePath(name).Error(licenseInfo.Error) + if parseErr != nil { + errorList.WithFilePath(name).Error(parseErr.Error()) + continue } } } diff --git a/pkg/linters/module/rules/license_parser.go b/pkg/linters/module/rules/license_parser.go index bd38c3ba..337be22d 100644 --- a/pkg/linters/module/rules/license_parser.go +++ b/pkg/linters/module/rules/license_parser.go @@ -18,6 +18,7 @@ package rules import ( "bufio" + "errors" "fmt" "os" "path/filepath" @@ -49,10 +50,8 @@ type FileTypeConfig struct { // LicenseInfo contains information about parsed license type LicenseInfo struct { - Type string // "CE", "EE", or empty - Year string // Extracted year - Valid bool // Whether license is valid - Error string // Error message if invalid + Type string // "CE", "EE", or empty + Year string // Extracted year } // LicenseParser handles license parsing and validation @@ -198,15 +197,14 @@ func initFileConfigs() map[string]FileTypeConfig { return configs } +var ErrUnsupportedFileType = fmt.Errorf("unsupported file type") + // ParseFile parses a file and extracts license information func (p *LicenseParser) ParseFile(filename string) (*LicenseInfo, error) { // Get file type configuration config := p.getFileConfig(filename) if config == nil { - return &LicenseInfo{ - Valid: false, - Error: fmt.Sprintf("unsupported file type: %s", filename), - }, nil + return nil, fmt.Errorf("%w: %s", ErrUnsupportedFileType, filename) } // Read file header @@ -215,35 +213,29 @@ func (p *LicenseParser) ParseFile(filename string) (*LicenseInfo, error) { if err != nil { return nil, fmt.Errorf("failed to read file: %w", err) } + // Check for generated file markers if isHeaderMarkedAsGenerated(header) { - return &LicenseInfo{Valid: true}, nil + return &LicenseInfo{}, nil } // Try to extract license text from header licenseText := p.extractLicenseText(header, config) if licenseText == "" { - return &LicenseInfo{ - Valid: false, - Error: "no license header found", - }, nil + return nil, errors.New("no license header found") } // Match against known licenses for _, license := range p.licenses { if matched, year := p.matchLicense(licenseText, license); matched { return &LicenseInfo{ - Type: license.Type, - Year: year, - Valid: true, + Type: license.Type, + Year: year, }, nil } } - return &LicenseInfo{ - Valid: false, - Error: "license header does not match any known license", - }, nil + return nil, errors.New("license header does not match any known license") } // getFileConfig returns the configuration for a given file diff --git a/pkg/linters/module/rules/license_parser_test.go b/pkg/linters/module/rules/license_parser_test.go index a14d35b8..213a19dd 100644 --- a/pkg/linters/module/rules/license_parser_test.go +++ b/pkg/linters/module/rules/license_parser_test.go @@ -29,7 +29,8 @@ func TestLicenseParser_ParseFile(t *testing.T) { name string content string filename string - want LicenseInfo + want *LicenseInfo + wantErr bool }{ { name: "Generated Go file is skipped (block comment)", @@ -40,9 +41,7 @@ Code Generated by some-tool v1.2.3. DO NOT EDIT. package main `, - want: LicenseInfo{ - Valid: true, - }, + want: &LicenseInfo{}, }, { name: "Generated Go file is skipped", @@ -51,25 +50,19 @@ package main package main `, - want: LicenseInfo{ - Valid: true, - }, + want: &LicenseInfo{}, }, { name: "Generated YAML file is skipped", filename: "values.yaml", content: "# This file was generated automatically. Do not edit by hand.\nkey: value\n", - want: LicenseInfo{ - Valid: true, - }, + want: &LicenseInfo{}, }, { name: "Generated block comment is skipped", filename: "script.sh", content: "# AUTO-GENERATED FILE - DO NOT modify\necho hello\n", - want: LicenseInfo{ - Valid: true, - }, + want: &LicenseInfo{}, }, { name: "Go file with CE license (block comment)", @@ -94,10 +87,9 @@ package main func main() {} `, - want: LicenseInfo{ - Type: "CE", - Year: "2024", - Valid: true, + want: &LicenseInfo{ + Type: "CE", + Year: "2024", }, }, { @@ -110,10 +102,9 @@ Licensed under the Deckhouse Platform Enterprise Edition (EE) license. See https package main `, - want: LicenseInfo{ - Type: "EE", - Year: "2023", - Valid: true, + want: &LicenseInfo{ + Type: "EE", + Year: "2023", }, }, { @@ -137,10 +128,9 @@ package main echo "Hello" `, - want: LicenseInfo{ - Type: "CE", - Year: "2025", - Valid: true, + want: &LicenseInfo{ + Type: "CE", + Year: "2025", }, }, { @@ -153,10 +143,9 @@ echo "Hello" def main(): pass `, - want: LicenseInfo{ - Type: "EE", - Year: "2023", - Valid: true, + want: &LicenseInfo{ + Type: "EE", + Year: "2023", }, }, { @@ -166,10 +155,8 @@ def main(): func main() {} `, - want: LicenseInfo{ - Valid: false, - Error: "no license header found", - }, + want: nil, + wantErr: true, }, { name: "File with invalid license", @@ -179,10 +166,8 @@ func main() {} package main `, - want: LicenseInfo{ - Valid: false, - Error: "license header does not match any known license", - }, + want: nil, + wantErr: true, }, { name: "Lua file with CE license", @@ -206,10 +191,9 @@ limitations under the License. local function test() end `, - want: LicenseInfo{ - Type: "CE", - Year: "2024", - Valid: true, + want: &LicenseInfo{ + Type: "CE", + Year: "2024", }, }, { @@ -221,10 +205,9 @@ end apiVersion: v1 kind: ConfigMap `, - want: LicenseInfo{ - Type: "EE", - Year: "2023", - Valid: true, + want: &LicenseInfo{ + Type: "EE", + Year: "2023", }, }, { @@ -246,10 +229,9 @@ kind: ConfigMap function main() {} `, - want: LicenseInfo{ - Type: "CE", - Year: "2024", - Valid: true, + want: &LicenseInfo{ + Type: "CE", + Year: "2024", }, }, } @@ -267,8 +249,12 @@ function main() {} // Parse file got, err := parser.ParseFile(tmpFile) - if err != nil { - t.Fatalf("ParseFile returned error: %v", err) + if (err != nil) != tt.wantErr { + t.Errorf("ParseFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantErr { + return } // Compare results @@ -278,12 +264,6 @@ function main() {} if got.Year != tt.want.Year { t.Errorf("Year = %v, want %v", got.Year, tt.want.Year) } - if got.Valid != tt.want.Valid { - t.Errorf("Valid = %v, want %v", got.Valid, tt.want.Valid) - } - if got.Error != tt.want.Error { - t.Errorf("Error = %v, want %v", got.Error, tt.want.Error) - } }) } } @@ -389,34 +369,30 @@ func TestLicenseParser_EdgeCases(t *testing.T) { name string content string filename string - want LicenseInfo + want *LicenseInfo + wantErr bool }{ { name: "Empty file", filename: "empty.go", content: "", - want: LicenseInfo{ - Valid: false, - Error: "no license header found", - }, + want: nil, + wantErr: true, }, { name: "File with only whitespace", filename: "whitespace.go", content: " \n\n\t\t \n ", - want: LicenseInfo{ - Valid: false, - Error: "no license header found", - }, + want: nil, + wantErr: true, }, { name: "License with Windows line endings", filename: "windows.go", content: "/*\r\nCopyright 2024 Flant JSC\r\n\r\nLicensed under the Apache License, Version 2.0 (the \"License\");\r\nyou may not use this file except in compliance with the License.\r\nYou may obtain a copy of the License at\r\n\r\n http://www.apache.org/licenses/LICENSE-2.0\r\n\r\nUnless required by applicable law or agreed to in writing, software\r\ndistributed under the License is distributed on an \"AS IS\" BASIS,\r\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\nSee the License for the specific language governing permissions and\r\nlimitations under the License.\r\n*/\r\n\r\npackage main\r\n", - want: LicenseInfo{ - Type: "CE", - Year: "2024", - Valid: true, + want: &LicenseInfo{ + Type: "CE", + Year: "2024", }, }, { @@ -429,10 +405,9 @@ Licensed under the Deckhouse Platform Enterprise Edition (EE) license. See https export function main() {} `, - want: LicenseInfo{ - Type: "EE", - Year: "2023", - Valid: true, + want: &LicenseInfo{ + Type: "EE", + Year: "2023", }, }, { @@ -454,10 +429,9 @@ export function main() {} #pragma once `, - want: LicenseInfo{ - Type: "CE", - Year: "2024", - Valid: true, + want: &LicenseInfo{ + Type: "CE", + Year: "2024", }, }, } @@ -475,8 +449,12 @@ export function main() {} // Parse file got, err := parser.ParseFile(tmpFile) - if err != nil { - t.Fatalf("ParseFile returned error: %v", err) + if (err != nil) != tt.wantErr { + t.Errorf("ParseFile() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantErr { + return } // Compare results @@ -486,12 +464,6 @@ export function main() {} if got.Year != tt.want.Year { t.Errorf("Year = %v, want %v", got.Year, tt.want.Year) } - if got.Valid != tt.want.Valid { - t.Errorf("Valid = %v, want %v", got.Valid, tt.want.Valid) - } - if got.Error != tt.want.Error { - t.Errorf("Error = %v, want %v", got.Error, tt.want.Error) - } }) } }