pkg/header/config.go (149 lines of code) (raw):

// 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 header import ( "fmt" "os" "regexp" "strconv" "strings" "time" "github.com/apache/skywalking-eyes/assets" "github.com/apache/skywalking-eyes/internal/logger" "github.com/apache/skywalking-eyes/pkg/comments" "github.com/apache/skywalking-eyes/pkg/license" "github.com/bmatcuk/doublestar/v2" ) type CommentOption string var ( Always CommentOption = "always" Never CommentOption = "never" OnFailure CommentOption = "on-failure" ASFNames = regexp.MustCompile("(?i)(the )?(Apache Software Foundation|ASF)") ) type LicenseConfig struct { SpdxID string `yaml:"spdx-id"` CopyrightOwner string `yaml:"copyright-owner"` CopyrightYear string `yaml:"copyright-year"` SoftwareName string `yaml:"software-name"` Content string `yaml:"content"` Pattern string `yaml:"pattern"` } type ConfigHeader struct { License LicenseConfig `yaml:"license"` Paths []string `yaml:"paths"` PathsIgnore []string `yaml:"paths-ignore"` Comment CommentOption `yaml:"comment"` // 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 int `yaml:"license-location-threshold"` Languages map[string]comments.Language `yaml:"language"` } // NormalizedLicense returns the normalized string of the license content, // "normalized" means the linebreaks and Punctuations are all trimmed. func (config *ConfigHeader) NormalizedLicense() string { return license.Normalize(config.GetLicenseContent()) } func (config *ConfigHeader) LicensePattern(style *comments.CommentStyle) *regexp.Regexp { pattern := config.License.Pattern if pattern == "" || strings.TrimSpace(pattern) == "" { return nil } // Trim leading and trailing newlines pattern = strings.TrimSpace(pattern) lines := strings.Split(pattern, "\n") for i, line := range lines { if line != "" { lines[i] = fmt.Sprintf("%v %v", style.Middle, line) } else { lines[i] = style.Middle } } lines = append(lines, "(("+style.Middle+"\n)*|\n*)") if style.Start != style.Middle { lines = append([]string{style.Start}, lines...) } if style.End != style.Middle { lines = append(lines, style.End) } pattern = strings.Join(lines, "\n") return regexp.MustCompile("(?s)" + pattern) } func (config *ConfigHeader) NormalizedPattern() *regexp.Regexp { pattern := config.License.Pattern if pattern == "" || strings.TrimSpace(pattern) == "" { return nil } pattern = license.NormalizePattern(pattern) return regexp.MustCompile("(?i).*" + pattern + ".*") } func (config *ConfigHeader) ShouldIgnore(path string) (bool, error) { matched, err := tryMatchPatten(path, config.Paths) if !matched || err != nil { return !matched, err } ignored, err := tryMatchPatten(path, config.PathsIgnore) if ignored || err != nil { return ignored, err } return false, nil } func tryMatchPatten(path string, patterns []string) (bool, error) { for _, pattern := range patterns { if m, err := doublestar.Match(pattern, path); m || err != nil { return m, err } } if stat, err := os.Stat(path); err == nil { for _, pattern := range patterns { pattern = strings.TrimRight(pattern, "/") if stat.Name() == pattern { return true, nil } pattern += "/" if strings.HasPrefix(path, pattern) { return true, nil } } } return false, nil } func (config *ConfigHeader) Finalize() error { if len(config.Paths) == 0 { config.Paths = []string{"**"} } comments.OverrideLanguageCommentStyle(config.Languages) logger.Log.Debugln("License header is:", config.NormalizedLicense()) if p := config.NormalizedPattern(); p != nil { logger.Log.Debugln("Pattern is:", p) } if config.LicenseLocationThreshold <= 0 { config.LicenseLocationThreshold = 80 } return nil } func (config *ConfigHeader) GetLicenseContent() (c string) { owner, name, year := config.License.CopyrightOwner, config.License.SoftwareName, config.License.CopyrightYear if year == "" { year = strconv.Itoa(time.Now().Year()) } defer func() { c = strings.ReplaceAll(c, "[year]", year) c = strings.ReplaceAll(c, "[owner]", owner) c = strings.ReplaceAll(c, "[software-name]", name) }() if c = strings.TrimSpace(config.License.Content); c != "" { return config.License.Content // Do not change anything in user config } c, err := readLicenseFromSpdx(config) if err != nil { logger.Log.Warnln(err) return "" } return c } func readLicenseFromSpdx(config *ConfigHeader) (string, error) { spdxID, owner := config.License.SpdxID, config.License.CopyrightOwner filename := fmt.Sprintf("header-templates/%v.txt", spdxID) if spdxID == "Apache-2.0" && ASFNames.MatchString(owner) { // Note that the Apache Software Foundation uses a different source header that is related to our use of a CLA. // Our instructions for our project's source headers are here (https://www.apache.org/legal/src-headers.html#headers). filename = "header-templates/Apache-2.0-ASF.txt" } content, err := assets.Asset(filename) if err != nil { return "", fmt.Errorf("failed to find a license template for spdx id %v, %w", spdxID, err) } return string(content), nil }