Skip to content

Commit 3c7695a

Browse files
committed
check copyright year and license type
1 parent bcc603c commit 3c7695a

File tree

4 files changed

+884
-8
lines changed

4 files changed

+884
-8
lines changed

tools/license-check/cmd/root.go

+31-7
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ import (
55
"os"
66
"path/filepath"
77
"strings"
8+
"time"
89

10+
"github.com/google/go-licenses/licenses"
911
"github.com/spf13/cobra"
1012
)
1113

1214
const rootCmdDesc = "Utilities for license check."
15+
const licenseConfidence = 0.9
1316

1417
type rootOptions struct {
1518
fileList []string
@@ -37,21 +40,18 @@ func (o *rootOptions) run() error {
3740
}
3841
foundErr := false
3942
for _, file := range o.fileList {
40-
b, err := os.ReadFile(file)
41-
if err != nil {
42-
return err
43-
}
4443
ext := filepath.Ext(file)
4544
if ext != ".tmpl" && ext != ".go" && ext != ".yaml" {
4645
continue
4746
}
48-
if !strings.Contains(string(b), `Licensed under the Apache License, Version 2.0 (the "License");`) {
49-
fmt.Fprintf(os.Stderr, "File %s does not contain Apache License.\n", file)
47+
48+
if err := checkLicenseType(file, time.Now().Year()); err != nil {
49+
fmt.Fprintf(os.Stderr, "File %s failed to pass license check %s.\n", file, err)
5050
foundErr = true
5151
}
5252
}
5353
if foundErr {
54-
return fmt.Errorf("found file missing license")
54+
return fmt.Errorf("found file failing license license")
5555
}
5656
return nil
5757
}
@@ -72,3 +72,27 @@ func Execute() {
7272
os.Exit(1)
7373
}
7474
}
75+
76+
func checkLicenseType(filePath string, year int) error {
77+
classifier, err := licenses.NewClassifier(licenseConfidence)
78+
if err != nil {
79+
return fmt.Errorf("failed to create license classifier: %w", err)
80+
}
81+
licenseName, _, err := classifier.Identify(filePath)
82+
if err != nil {
83+
return fmt.Errorf("failed to identify license for %s: %w", filePath, err)
84+
}
85+
if !strings.Contains(licenseName, "Apache") {
86+
return fmt.Errorf("found license type %s, expect Apache", licenseName)
87+
}
88+
89+
b, err := os.ReadFile(filePath)
90+
if err != nil {
91+
return err
92+
}
93+
expectStr := fmt.Sprintf("Copyright %d Google", year)
94+
if !strings.Contains(strings.ToLower(string(b)), strings.ToLower(expectStr)) {
95+
return fmt.Errorf("expected copyright string %q not found", expectStr)
96+
}
97+
return nil
98+
}

tools/license-check/cmd/root_test.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"testing"
7+
"time"
8+
)
9+
10+
const validApache2LicenseFormat string = `
11+
Licensed under the Apache License, Version 2.0 (the "License");
12+
you may not use this file except in compliance with the License.
13+
You may obtain a copy of the License at
14+
15+
http://www.apache.org/licenses/LICENSE-2.0
16+
17+
Unless required by applicable law or agreed to in writing, software
18+
distributed under the License is distributed on an "AS IS" BASIS,
19+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20+
See the License for the specific language governing permissions and
21+
limitations under the License.
22+
`
23+
const validCopyright string = "Copyright %d Google Inc\n"
24+
const invalidCopyright string = "Copyright 1900 Google Inc\n"
25+
const invalidLicense string = "not a license"
26+
27+
func TestCheckLicenseFail(t *testing.T) {
28+
dir := t.TempDir()
29+
tests := []struct {
30+
name string
31+
content string
32+
wantErr bool
33+
}{
34+
{
35+
name: "success",
36+
content: fmt.Sprintf(validCopyright, time.Now().Year()) + validApache2LicenseFormat,
37+
},
38+
{
39+
name: "wrong year copyright",
40+
content: invalidCopyright + validApache2LicenseFormat,
41+
wantErr: true,
42+
},
43+
{
44+
name: "invalid license",
45+
content: fmt.Sprintf(validCopyright, time.Now().Year()) + invalidLicense,
46+
wantErr: true,
47+
},
48+
}
49+
for _, tc := range tests {
50+
f, err := os.CreateTemp(dir, "testfile")
51+
if err != nil {
52+
t.Fatal(err)
53+
}
54+
if _, err := f.WriteString(tc.content); err != nil {
55+
t.Fatal(err)
56+
}
57+
err = checkLicenseType(f.Name(), time.Now().Year())
58+
if err == nil && tc.wantErr {
59+
t.Errorf("checkLicenseType() want err, but got nil")
60+
}
61+
if err != nil && !tc.wantErr {
62+
t.Errorf("checkLicenseType() got error %s", err)
63+
}
64+
}
65+
}

tools/license-check/go.mod

+24-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,32 @@ go 1.23.0
44

55
toolchain go1.23.1
66

7-
require github.com/spf13/cobra v1.9.1
7+
require (
8+
github.com/google/go-licenses v1.6.0
9+
github.com/spf13/cobra v1.9.1
10+
)
811

912
require (
13+
github.com/emirpasic/gods v1.12.0 // indirect
14+
github.com/go-logr/logr v1.2.0 // indirect
15+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
16+
github.com/google/licenseclassifier v0.0.0-20210722185704-3043a050f148 // indirect
1017
github.com/inconshreveable/mousetrap v1.1.0 // indirect
18+
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
19+
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
20+
github.com/mitchellh/go-homedir v1.1.0 // indirect
21+
github.com/sergi/go-diff v1.2.0 // indirect
1122
github.com/spf13/pflag v1.0.6 // indirect
23+
github.com/src-d/gcfg v1.4.0 // indirect
24+
github.com/xanzy/ssh-agent v0.2.1 // indirect
25+
go.opencensus.io v0.23.0 // indirect
26+
golang.org/x/crypto v0.1.0 // indirect
27+
golang.org/x/mod v0.7.0 // indirect
28+
golang.org/x/net v0.5.0 // indirect
29+
golang.org/x/sys v0.4.0 // indirect
30+
golang.org/x/tools v0.5.0 // indirect
31+
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
32+
gopkg.in/src-d/go-git.v4 v4.13.1 // indirect
33+
gopkg.in/warnings.v0 v0.1.2 // indirect
34+
k8s.io/klog/v2 v2.80.1 // indirect
1235
)

0 commit comments

Comments
 (0)