Skip to content

Commit

Permalink
feat: allow comments before the license header
Browse files Browse the repository at this point in the history
Useful for controller-gen and other code generators.

Signed-off-by: Alexey Palazhchenko <[email protected]>
  • Loading branch information
AlekSi committed Sep 6, 2021
1 parent 9a470a1 commit 6caaabc
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 18 deletions.
1 change: 1 addition & 0 deletions .conform.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ policies:
excludeSuffixes:
- .pb.go
- .pb.gw.go
allowPrecedingComments: true
header: |
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ policies:
- .ext
excludeSuffixes:
- .exclude-ext-prefix.ext
allowPrecedingComments: false
header: |
This is the contents of a license header.
```
Expand Down
4 changes: 2 additions & 2 deletions cmd/conform/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ var serveCmd = &cobra.Command{

return
}
//nolint: errcheck
defer os.RemoveAll(dir)

defer os.RemoveAll(dir) //nolint:errcheck

if err = os.MkdirAll(filepath.Join(dir, "github"), 0o700); err != nil {
log.Printf("failed to create github directory: %+v\n", err)
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/talos-systems/conform

go 1.13

require (
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/deckarep/golang-set v1.7.1 // indirect
Expand All @@ -18,11 +20,10 @@ require (
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.7.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gonum.org/v1/gonum v0.6.1 // indirect
gopkg.in/jdkato/prose.v2 v2.0.0-20180825173540-767a23049b9e
gopkg.in/neurosnap/sentences.v1 v1.0.6 // indirect
gopkg.in/yaml.v2 v2.3.0
)

go 1.13
3 changes: 1 addition & 2 deletions internal/enforcer/enforcer.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ func (c *Conform) Enforce(setters ...policy.Option) error {
}
}

//nolint: errcheck
w.Flush()
w.Flush() //nolint:errcheck

if !pass {
return errors.New("1 or more policy failed")
Expand Down
2 changes: 1 addition & 1 deletion internal/policy/commit/check_jira_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//nolint: testpackage
//nolint:testpackage
package commit

import (
Expand Down
2 changes: 1 addition & 1 deletion internal/policy/commit/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

//nolint: testpackage
//nolint:testpackage
package commit

import (
Expand Down
72 changes: 62 additions & 10 deletions internal/policy/license/license.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package license

import (
"bufio"
"bytes"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -33,6 +34,10 @@ type License struct {
// ExcludeSuffixes is the Suffixes used to find files that the license policy
// should not be applied to.
ExcludeSuffixes []string `mapstructure:"excludeSuffixes"`
// AllowPrecedingComments, when enabled, allows blank lines and `//` and `#` line comments
// before the license header. Useful for code generators that put build constraints or
// "DO NOT EDIT" lines before the license.
AllowPrecedingComments bool `mapstructure:"allowPrecedingComments"`
// Header is the contents of the license header.
Header string `mapstructure:"header"`
}
Expand Down Expand Up @@ -95,7 +100,7 @@ func (l License) ValidateLicenseHeader() policy.Check {
return check
}

value := []byte(l.Header)
value := []byte(strings.TrimSpace(l.Header))

err := filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
Expand Down Expand Up @@ -124,18 +129,15 @@ func (l License) ValidateLicenseHeader() policy.Check {
// Check files matching the included suffixes.
for _, suffix := range l.IncludeSuffixes {
if strings.HasSuffix(info.Name(), suffix) {
var contents []byte
if contents, err = ioutil.ReadFile(path); err != nil {
check.errors = append(check.errors, errors.Errorf("Failed to open %s", path))

return nil //nolint:nilerr
if l.AllowPrecedingComments {
err = validateFileWithPrecedingComments(path, value)
} else {
err = validateFile(path, value)
}

if bytes.HasPrefix(contents, value) {
continue
if err != nil {
check.errors = append(check.errors, err)
}

check.errors = append(check.errors, errors.Errorf("File %s does not contain a license header", info.Name()))
}
}
}
Expand All @@ -148,3 +150,53 @@ func (l License) ValidateLicenseHeader() policy.Check {

return check
}

func validateFile(path string, value []byte) error {
contents, err := ioutil.ReadFile(path)
if err != nil {
return errors.Errorf("Failed to read %s: %s", path, err)
}

if bytes.HasPrefix(contents, value) {
return nil
}

return errors.Errorf("File %s does not contain a license header", path)
}

func validateFileWithPrecedingComments(path string, value []byte) error {
f, err := os.Open(path)
if err != nil {
return errors.Errorf("Failed to open %s: %s", path, err)
}
defer f.Close() //nolint:errcheck

var contents []byte

// read lines until the first non-comment line
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())

comment := line == ""
comment = comment || strings.HasPrefix(line, "//")
comment = comment || strings.HasPrefix(line, "#")

if !comment {
break
}

contents = append(contents, scanner.Bytes()...)
contents = append(contents, '\n')
}

if err := scanner.Err(); err != nil {
return errors.Errorf("Failed to check file %s: %s", path, err)
}

if bytes.Contains(contents, value) {
return nil
}

return errors.Errorf("File %s does not contain a license header", path)
}
43 changes: 43 additions & 0 deletions internal/policy/license/license_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//go:build !some_test_tag
// +build !some_test_tag

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package license_test

import (
"testing"

"github.com/stretchr/testify/assert"

"github.com/talos-systems/conform/internal/policy/license"
)

func TestLicense(t *testing.T) {
const header = `
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.`

t.Run("Default", func(t *testing.T) {
l := license.License{
IncludeSuffixes: []string{".go"},
AllowPrecedingComments: false,
Header: header,
}
check := l.ValidateLicenseHeader()
assert.Equal(t, "Found 1 files without license header", check.Message())
})

t.Run("AllowPrecedingComments", func(t *testing.T) {
l := license.License{
IncludeSuffixes: []string{".go"},
AllowPrecedingComments: true,
Header: header,
}
check := l.ValidateLicenseHeader()
assert.Equal(t, "All files have a valid license header", check.Message())
})
}

0 comments on commit 6caaabc

Please sign in to comment.