Skip to content

Commit

Permalink
Implement -check flag
Browse files Browse the repository at this point in the history
When this flag is used:

* The program never modifies any files
* If all files in the pattern contain a license, the program exits with
a zero exit code
* If at least one file in the pattern requires modification to include
license text, the program prints such files to STDOUT and exits with
a non-zero exit code
  • Loading branch information
mithun committed Feb 18, 2020
1 parent c464135 commit 27146d5
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 32 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ to any file that already has one.
-f custom license file (no default)
-l license type: apache, bsd, mit (defaults to "apache")
-y year (defaults to current year)
-check check only mode: verify presence of license headers and exit with non-zero code if missing

The pattern argument can be provided multiple times, and may also refer
to single files.
Expand Down
104 changes: 72 additions & 32 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package main

import (
"bytes"
"errors"
"flag"
"fmt"
"html/template"
Expand Down Expand Up @@ -46,11 +47,12 @@ Flags:
`

var (
holder = flag.String("c", "Google LLC", "copyright holder")
license = flag.String("l", "apache", "license type: apache, bsd, mit")
licensef = flag.String("f", "", "license file")
year = flag.String("y", fmt.Sprint(time.Now().Year()), "copyright year(s)")
verbose = flag.Bool("v", false, "verbose mode: print the name of the files that are modified")
holder = flag.String("c", "Google LLC", "copyright holder")
license = flag.String("l", "apache", "license type: apache, bsd, mit")
licensef = flag.String("f", "", "license file")
year = flag.String("y", fmt.Sprint(time.Now().Year()), "copyright year(s)")
verbose = flag.Bool("v", false, "verbose mode: print the name of the files that are modified")
checkonly = flag.Bool("check", false, "check only mode: verify presence of license headers and exit with non-zero code if missing")
)

func main() {
Expand Down Expand Up @@ -97,13 +99,35 @@ func main() {
for f := range ch {
f := f // https://golang.org/doc/faq#closures_and_goroutines
wg.Go(func() error {
modified, err := addLicense(f.path, f.mode, t, data)
if err != nil {
log.Printf("%s: %v", f.path, err)
return err
}
if *verbose && modified {
log.Printf("%s modified", f.path)
if *checkonly {
// Check if file extension is known
lic, err := licenseHeader(f.path, t, data)
if err != nil {
log.Printf("%s: %v", f.path, err)
return err
}
if lic == nil { // Unknown fileExtension
return nil
}
// Check if file has a license
isMissingLicenseHeader, err := fileHasLicense(f.path)
if err != nil {
log.Printf("%s: %v", f.path, err)
return err
}
if isMissingLicenseHeader {
fmt.Printf("%s\n", f.path)
return errors.New("missing license header")
}
} else {
modified, err := addLicense(f.path, f.mode, t, data)
if err != nil {
log.Printf("%s: %v", f.path, err)
return err
}
if *verbose && modified {
log.Printf("%s modified", f.path)
}
}
return nil
})
Expand Down Expand Up @@ -142,11 +166,45 @@ func walk(ch chan<- *file, start string) {
}

func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data *copyrightData) (bool, error) {
var lic []byte
var err error
lic, err = licenseHeader(path, tmpl, data)
if err != nil || lic == nil {
return false, err
}

b, err := ioutil.ReadFile(path)
if err != nil || hasLicense(b) {
return false, err
}

line := hashBang(b)
if len(line) > 0 {
b = b[len(line):]
if line[len(line)-1] != '\n' {
line = append(line, '\n')
}
lic = append(line, lic...)
}
b = append(lic, b...)
return true, ioutil.WriteFile(path, b, fmode)
}

// fileHasLicense reports whether the file at path contains a license header.
func fileHasLicense(path string) (bool, error) {
b, err := ioutil.ReadFile(path)
if err != nil || hasLicense(b) {
return false, err
}
return true, nil
}

func licenseHeader(path string, tmpl *template.Template, data *copyrightData) ([]byte, error) {
var lic []byte
var err error
switch fileExtension(path) {
default:
return false, nil
return nil, nil
case ".c", ".h":
lic, err = prefix(tmpl, data, "/*", " * ", " */")
case ".js", ".jsx", ".tsx", ".css", ".tf", ".ts":
Expand All @@ -168,25 +226,7 @@ func addLicense(path string, fmode os.FileMode, tmpl *template.Template, data *c
case ".ml", ".mli", ".mll", ".mly":
lic, err = prefix(tmpl, data, "(**", " ", "*)")
}
if err != nil || lic == nil {
return false, err
}

b, err := ioutil.ReadFile(path)
if err != nil || hasLicense(b) {
return false, err
}

line := hashBang(b)
if len(line) > 0 {
b = b[len(line):]
if line[len(line)-1] != '\n' {
line = append(line, '\n')
}
lic = append(line, lic...)
}
b = append(lic, b...)
return true, ioutil.WriteFile(path, b, fmode)
return lic, err
}

func fileExtension(name string) string {
Expand Down
45 changes: 45 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,48 @@ func TestReadErrors(t *testing.T) {
}
run(t, "chmod", "0644", samplefile)
}

func TestCheckSuccess(t *testing.T) {
if os.Getenv("RUNME") != "" {
main()
return
}

tmp := tempDir(t)
t.Logf("tmp dir: %s", tmp)
samplefile := filepath.Join(tmp, "file.c")

run(t, "cp", "testdata/expected/file.c", samplefile)
cmd := exec.Command(os.Args[0],
"-test.run=TestCheckSuccess",
"-l", "apache", "-c", "Google LLC", "-y", "2018",
"-check", samplefile,
)
cmd.Env = []string{"RUNME=1"}
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("%v\n%s", err, out)
}
}

func TestCheckFail(t *testing.T) {
if os.Getenv("RUNME") != "" {
main()
return
}

tmp := tempDir(t)
t.Logf("tmp dir: %s", tmp)
samplefile := filepath.Join(tmp, "file.c")

run(t, "cp", "testdata/initial/file.c", samplefile)
cmd := exec.Command(os.Args[0],
"-test.run=TestCheckFail",
"-l", "apache", "-c", "Google LLC", "-y", "2018",
"-check", samplefile,
)
cmd.Env = []string{"RUNME=1"}
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatalf("TestCheckFail exited with a zero exit code.\n%s", out)
}
}

0 comments on commit 27146d5

Please sign in to comment.