github.com/apache/skywalking-eyes@v0.6.0/pkg/header/config.go (about) 1 // Licensed to the Apache Software Foundation (ASF) under one 2 // or more contributor license agreements. See the NOTICE file 3 // distributed with this work for additional information 4 // regarding copyright ownership. The ASF licenses this file 5 // to you under the Apache License, Version 2.0 (the 6 // "License"); you may not use this file except in compliance 7 // with the License. You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package header 19 20 import ( 21 "fmt" 22 "os" 23 "regexp" 24 "strconv" 25 "strings" 26 "time" 27 28 "github.com/apache/skywalking-eyes/assets" 29 "github.com/apache/skywalking-eyes/internal/logger" 30 "github.com/apache/skywalking-eyes/pkg/comments" 31 "github.com/apache/skywalking-eyes/pkg/license" 32 33 "github.com/bmatcuk/doublestar/v2" 34 ) 35 36 type CommentOption string 37 38 var ( 39 Always CommentOption = "always" 40 Never CommentOption = "never" 41 OnFailure CommentOption = "on-failure" 42 43 ASFNames = regexp.MustCompile("(?i)(the )?(Apache Software Foundation|ASF)") 44 ) 45 46 type LicenseConfig struct { 47 SpdxID string `yaml:"spdx-id"` 48 CopyrightOwner string `yaml:"copyright-owner"` 49 CopyrightYear string `yaml:"copyright-year"` 50 SoftwareName string `yaml:"software-name"` 51 Content string `yaml:"content"` 52 Pattern string `yaml:"pattern"` 53 } 54 55 type ConfigHeader struct { 56 License LicenseConfig `yaml:"license"` 57 Paths []string `yaml:"paths"` 58 PathsIgnore []string `yaml:"paths-ignore"` 59 Comment CommentOption `yaml:"comment"` 60 61 // LicenseLocationThreshold specifies the index threshold where the license header can be located, 62 // after all, a "header" cannot be TOO far from the file start. 63 LicenseLocationThreshold int `yaml:"license-location-threshold"` 64 Languages map[string]comments.Language `yaml:"language"` 65 } 66 67 // NormalizedLicense returns the normalized string of the license content, 68 // "normalized" means the linebreaks and Punctuations are all trimmed. 69 func (config *ConfigHeader) NormalizedLicense() string { 70 return license.Normalize(config.GetLicenseContent()) 71 } 72 73 func (config *ConfigHeader) LicensePattern(style *comments.CommentStyle) *regexp.Regexp { 74 pattern := config.License.Pattern 75 76 if pattern == "" || strings.TrimSpace(pattern) == "" { 77 return nil 78 } 79 80 // Trim leading and trailing newlines 81 pattern = strings.TrimSpace(pattern) 82 lines := strings.Split(pattern, "\n") 83 for i, line := range lines { 84 if line != "" { 85 lines[i] = fmt.Sprintf("%v %v", style.Middle, line) 86 } else { 87 lines[i] = style.Middle 88 } 89 } 90 91 lines = append(lines, "(("+style.Middle+"\n)*|\n*)") 92 93 if style.Start != style.Middle { 94 lines = append([]string{style.Start}, lines...) 95 } 96 97 if style.End != style.Middle { 98 lines = append(lines, style.End) 99 } 100 101 pattern = strings.Join(lines, "\n") 102 103 return regexp.MustCompile("(?s)" + pattern) 104 } 105 106 func (config *ConfigHeader) NormalizedPattern() *regexp.Regexp { 107 pattern := config.License.Pattern 108 109 if pattern == "" || strings.TrimSpace(pattern) == "" { 110 return nil 111 } 112 113 pattern = license.NormalizePattern(pattern) 114 115 return regexp.MustCompile("(?i).*" + pattern + ".*") 116 } 117 118 func (config *ConfigHeader) ShouldIgnore(path string) (bool, error) { 119 matched, err := tryMatchPatten(path, config.Paths) 120 if !matched || err != nil { 121 return !matched, err 122 } 123 124 ignored, err := tryMatchPatten(path, config.PathsIgnore) 125 if ignored || err != nil { 126 return ignored, err 127 } 128 129 return false, nil 130 } 131 132 func tryMatchPatten(path string, patterns []string) (bool, error) { 133 for _, pattern := range patterns { 134 if m, err := doublestar.Match(pattern, path); m || err != nil { 135 return m, err 136 } 137 } 138 139 if stat, err := os.Stat(path); err == nil { 140 for _, pattern := range patterns { 141 pattern = strings.TrimRight(pattern, "/") 142 if stat.Name() == pattern { 143 return true, nil 144 } 145 pattern += "/" 146 if strings.HasPrefix(path, pattern) { 147 return true, nil 148 } 149 } 150 } 151 152 return false, nil 153 } 154 155 func (config *ConfigHeader) Finalize() error { 156 if len(config.Paths) == 0 { 157 config.Paths = []string{"**"} 158 } 159 160 comments.OverrideLanguageCommentStyle(config.Languages) 161 162 logger.Log.Debugln("License header is:", config.NormalizedLicense()) 163 164 if p := config.NormalizedPattern(); p != nil { 165 logger.Log.Debugln("Pattern is:", p) 166 } 167 168 if config.LicenseLocationThreshold <= 0 { 169 config.LicenseLocationThreshold = 80 170 } 171 172 return nil 173 } 174 175 func (config *ConfigHeader) GetLicenseContent() (c string) { 176 owner, name, year := config.License.CopyrightOwner, config.License.SoftwareName, config.License.CopyrightYear 177 if year == "" { 178 year = strconv.Itoa(time.Now().Year()) 179 } 180 181 defer func() { 182 c = strings.ReplaceAll(c, "[year]", year) 183 c = strings.ReplaceAll(c, "[owner]", owner) 184 c = strings.ReplaceAll(c, "[software-name]", name) 185 }() 186 187 if c = strings.TrimSpace(config.License.Content); c != "" { 188 return config.License.Content // Do not change anything in user config 189 } 190 c, err := readLicenseFromSpdx(config) 191 if err != nil { 192 logger.Log.Warnln(err) 193 return "" 194 } 195 196 return c 197 } 198 199 func readLicenseFromSpdx(config *ConfigHeader) (string, error) { 200 spdxID, owner := config.License.SpdxID, config.License.CopyrightOwner 201 filename := fmt.Sprintf("header-templates/%v.txt", spdxID) 202 203 if spdxID == "Apache-2.0" && ASFNames.MatchString(owner) { 204 // Note that the Apache Software Foundation uses a different source header that is related to our use of a CLA. 205 // Our instructions for our project's source headers are here (https://www.apache.org/legal/src-headers.html#headers). 206 filename = "header-templates/Apache-2.0-ASF.txt" 207 } 208 209 content, err := assets.Asset(filename) 210 if err != nil { 211 return "", fmt.Errorf("failed to find a license template for spdx id %v, %w", spdxID, err) 212 } 213 return string(content), nil 214 }