diff --git a/.mage/header.txt b/.mage/header.txt deleted file mode 100644 index 65243ee473..0000000000 --- a/.mage/header.txt +++ /dev/null @@ -1,13 +0,0 @@ -Copyright © 2019 The Things Network Foundation, The Things Industries B.V. - -Licensed 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. diff --git a/.mage/header.yml b/.mage/header.yml new file mode 100644 index 0000000000..29fbfd7296 --- /dev/null +++ b/.mage/header.yml @@ -0,0 +1,21 @@ +rules: +- include: [.go,.js,.make,Makefile,.styl,.proto] + exclude: [.snap,.json,.goreleaser.yml] + header: + | + Copyright © 2019 The Things Network Foundation, The Things Industries B.V. + + Licensed 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. + prefix: '// ' +- include: [.make,Makefile] + prefix: '# ' diff --git a/.mage/headers.go b/.mage/headers.go index ffb9b146aa..88fc63ddd9 100644 --- a/.mage/headers.go +++ b/.mage/headers.go @@ -16,6 +16,7 @@ package ttnmage import ( "bufio" + "bytes" "fmt" "io/ioutil" "os" @@ -23,56 +24,81 @@ import ( "strings" "github.com/magefile/mage/mg" + "gopkg.in/yaml.v2" ) // Headers namespace. type Headers mg.Namespace -var fileCommentPrefix = map[string]string{ - ".go": "// ", - ".js": "// ", - ".make": "# ", - ".styl": "// ", - ".proto": "// ", +// HeaderRule in the header config file. +type HeaderRule struct { + Include []string `yaml:"include"` + Exclude []string `yaml:"exclude"` + Header string `yaml:"header"` + headerLines [][]byte + Prefix string `yaml:"prefix"` } -type fileFilter struct { - exclude []string - include []string +func (r *HeaderRule) split() { + r.headerLines = bytes.Split([]byte(strings.TrimSpace(r.Header)), []byte("\n")) } -func (f fileFilter) match(search string) (match bool) { - if len(f.include) > 0 { - for _, item := range f.include { - if strings.Contains(search, item) { +func (r *HeaderRule) match(path string) (match bool) { + if len(r.Include) > 0 { + for _, item := range r.Include { + if strings.Contains(path, item) { match = true } } } else { match = true } - for _, item := range f.exclude { - if strings.Contains(search, item) { + for _, item := range r.Exclude { + if strings.Contains(path, item) { return false } } return } +// HeaderConfig is the format of the header configuration file. +type HeaderConfig struct { + Rules []*HeaderRule `yaml:"rules"` +} + +func (c *HeaderConfig) split() { + for _, rule := range c.Rules { + rule.split() + } +} + +func (c *HeaderConfig) get(filename string) (r *HeaderRule) { + for _, rule := range c.Rules { + if !rule.match(filename) { + continue + } + if r == nil { + r = &HeaderRule{} + } + if rule.Header != "" { + r.Header, r.headerLines = rule.Header, rule.headerLines + } + if rule.Prefix != "" { + r.Prefix = rule.Prefix + } + } + return r +} + var ( - headerFilter fileFilter headerFile string - headerLines []string + headerConfig HeaderConfig ) func init() { - headerFilter = fileFilter{ - exclude: strings.Fields(os.Getenv("HEADER_EXCLUDE")), - include: strings.Fields(os.Getenv("HEADER_INCLUDE")), - } headerFile = os.Getenv("HEADER_FILE") if headerFile == "" { - headerFile = ".mage/header.txt" + headerFile = ".mage/header.yml" } } @@ -81,7 +107,10 @@ func (Headers) loadFile() error { if err != nil { return err } - headerLines = strings.Split(strings.TrimSpace(string(headerBytes)), "\n") + if err = yaml.Unmarshal(headerBytes, &headerConfig); err != nil { + return err + } + headerConfig.split() return nil } @@ -94,27 +123,30 @@ func (err checkErr) Error() string { return fmt.Sprintf("%s %s", err.Path, err.Reason) } -func (h Headers) check(path, commentPrefix string) error { +func (h Headers) check(path string) error { + rule := headerConfig.get(path) + if rule == nil { + return nil + } f, err := os.Open(path) if err != nil { return err } defer f.Close() s := bufio.NewScanner(f) - for i, expected := range headerLines { - expected = strings.TrimSpace(commentPrefix + expected) + for i, expected := range rule.headerLines { if !s.Scan() { return &checkErr{Path: path, Reason: "has less lines than expected header"} } - line := s.Text() - if i == 0 && strings.Contains(line, "generated") { + line := s.Bytes() + if i == 0 && bytes.Contains(line, []byte("generated")) { return nil // Skip generated files. } - if s.Text() != expected { - return &checkErr{Path: path, Reason: fmt.Sprintf("did not contain expected line: %s", expected)} + if !bytes.Equal(bytes.TrimPrefix(line, []byte(rule.Prefix)), expected) && !bytes.Equal(line, bytes.TrimSpace([]byte(rule.Prefix))) { + return &checkErr{Path: path, Reason: fmt.Sprintf("did not contain expected header line: %s", string(expected))} } } - if s.Scan() && s.Text() != "" { + if s.Scan() && len(s.Bytes()) != 0 { return &checkErr{Path: path, Reason: "does not have empty line after header"} } return nil @@ -153,13 +185,8 @@ func (h Headers) Check() error { if selectedFiles != nil && !selectedFiles[path] { return nil } - if !headerFilter.match(path) { - return nil - } - if prefix, ok := fileCommentPrefix[filepath.Ext(path)]; ok { - if checkErr := h.check(path, prefix); checkErr != nil { - checkErrs = append(checkErrs, checkErr) - } + if checkErr := h.check(path); checkErr != nil { + checkErrs = append(checkErrs, checkErr) } return nil })