Skip to content

Commit

Permalink
Merge pull request #623 from TheThingsNetwork/feature/headers-check
Browse files Browse the repository at this point in the history
Use config file for headers check
  • Loading branch information
htdvisser committed May 3, 2019
2 parents 74fade6 + c974711 commit 711d05b
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 51 deletions.
13 changes: 0 additions & 13 deletions .mage/header.txt

This file was deleted.

21 changes: 21 additions & 0 deletions .mage/header.yml
Original file line number Diff line number Diff line change
@@ -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: '# '
103 changes: 65 additions & 38 deletions .mage/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,63 +16,89 @@ package ttnmage

import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"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"
}
}

Expand All @@ -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
}

Expand All @@ -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
Expand Down Expand Up @@ -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
})
Expand Down

0 comments on commit 711d05b

Please sign in to comment.