Skip to content

Commit 5eedde1

Browse files
authored
add report subcommand with custom templates (#141)
The new report subcommand replaces the existing csv command (and the csv command is now deprecated). By default `go-licenses report` will output CSV, but it also allows for a custom go template to be used.
1 parent 2aec38b commit 5eedde1

File tree

6 files changed

+219
-68
lines changed

6 files changed

+219
-68
lines changed

README.md

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ If you were using `go get` to install this tool, note that
3939
## Reports
4040

4141
```shell
42-
$ go-licenses csv github.com/google/go-licenses
42+
$ go-licenses report github.com/google/go-licenses
4343
W0410 06:02:57.077781 31529 library.go:86] "golang.org/x/sys/unix" contains non-Go code that can't be inspected for further dependencies:
4444
/home/username/go/pkg/mod/golang.org/x/[email protected]/unix/asm_linux_amd64.s
4545
W0410 06:02:59.476443 31529 library.go:86] "golang.org/x/crypto/curve25519/internal/field" contains non-Go code that can't be inspected for further dependencies:
@@ -83,21 +83,76 @@ share a license file.
8383
8484
URLs are versioned based on go modules metadata.
8585
86-
**Tip**: go-licenses writes CSV to stdout and info/warnings/errors logs to stderr.
87-
To save the CSV to a file `licenses.csv` in bash, run:
86+
**Tip**: go-licenses writes the report to stdout and info/warnings/errors logs
87+
to stderr. To save the CSV to a file `licenses.csv` in bash, run:
8888
8989
```bash
90-
go-licenses csv github.com/google/go-licenses > licenses.csv
90+
go-licenses report github.com/google/go-licenses > licenses.csv
9191
```
9292
9393
Or, to also save error logs to an `errors` file, run:
9494
9595
```bash
96-
go-licenses csv github.com/google/go-licenses > licenses.csv 2> errors
96+
go-licenses report github.com/google/go-licenses > licenses.csv 2> errors
9797
```
9898
9999
**Note**: some warnings and errors may be expected, refer to [Warnings and Errors](#warnings-and-errors) for more information.
100100
101+
## Reports with Custom Templates
102+
103+
```shell
104+
go-licenses report github.com/google/go-licenses --template testdata/modules/hello01/licenses.tpl
105+
W0822 16:56:50.696198 10200 library.go:94] "golang.org/x/sys/unix" contains non-Go code that can't be inspected for further dependencies:
106+
/Users/willnorris/go/pkg/mod/golang.org/x/[email protected]/unix/asm_bsd_arm64.s
107+
/Users/willnorris/go/pkg/mod/golang.org/x/[email protected]/unix/zsyscall_darwin_arm64.1_13.s
108+
/Users/willnorris/go/pkg/mod/golang.org/x/[email protected]/unix/zsyscall_darwin_arm64.s
109+
W0822 16:56:51.466449 10200 library.go:94] "golang.org/x/crypto/chacha20" contains non-Go code that can't be inspected for further dependencies:
110+
/Users/willnorris/go/pkg/mod/golang.org/x/[email protected]/chacha20/chacha_arm64.s
111+
W0822 16:56:51.475139 10200 library.go:94] "golang.org/x/crypto/curve25519/internal/field" contains non-Go code that can't be inspected for further dependencies:
112+
/Users/willnorris/go/pkg/mod/golang.org/x/[email protected]/curve25519/internal/field/fe_arm64.s
113+
W0822 16:56:51.602250 10200 library.go:269] module github.com/google/go-licenses has empty version, defaults to HEAD. The license URL may be incorrect. Please verify!
114+
W0822 16:56:51.605074 10200 library.go:269] module github.com/google/go-licenses has empty version, defaults to HEAD. The license URL may be incorrect. Please verify!
115+
116+
- github.com/emirpasic/gods ([BSD-2-Clause](https://github.com/emirpasic/gods/blob/v1.12.0/LICENSE))
117+
- github.com/golang/glog ([Apache-2.0](https://github.com/golang/glog/blob/23def4e6c14b/LICENSE))
118+
- github.com/golang/groupcache/lru ([Apache-2.0](https://github.com/golang/groupcache/blob/41bb18bfe9da/LICENSE))
119+
- github.com/google/go-licenses ([Apache-2.0](https://github.com/google/go-licenses/blob/HEAD/LICENSE))
120+
- github.com/google/go-licenses/internal/third_party/pkgsite ([BSD-3-Clause](https://github.com/google/go-licenses/blob/HEAD/internal/third_party/pkgsite/LICENSE))
121+
- github.com/google/licenseclassifier ([Apache-2.0](https://github.com/google/licenseclassifier/blob/3043a050f148/LICENSE))
122+
- github.com/google/licenseclassifier/licenses ([Unlicense](https://github.com/google/licenseclassifier/blob/3043a050f148/licenses/Unlicense.txt))
123+
- github.com/google/licenseclassifier/stringclassifier ([Apache-2.0](https://github.com/google/licenseclassifier/blob/3043a050f148/stringclassifier/LICENSE))
124+
- github.com/jbenet/go-context/io ([MIT](https://github.com/jbenet/go-context/blob/d14ea06fba99/LICENSE))
125+
- github.com/kevinburke/ssh_config ([MIT](https://github.com/kevinburke/ssh_config/blob/01f96b0aa0cd/LICENSE))
126+
- github.com/mitchellh/go-homedir ([MIT](https://github.com/mitchellh/go-homedir/blob/v1.1.0/LICENSE))
127+
- github.com/otiai10/copy ([MIT](https://github.com/otiai10/copy/blob/v1.6.0/LICENSE))
128+
- github.com/sergi/go-diff/diffmatchpatch ([MIT](https://github.com/sergi/go-diff/blob/v1.2.0/LICENSE))
129+
- github.com/spf13/cobra ([Apache-2.0](https://github.com/spf13/cobra/blob/v1.5.0/LICENSE.txt))
130+
- github.com/spf13/pflag ([BSD-3-Clause](https://github.com/spf13/pflag/blob/v1.0.5/LICENSE))
131+
- github.com/src-d/gcfg ([BSD-3-Clause](https://github.com/src-d/gcfg/blob/v1.4.0/LICENSE))
132+
- github.com/xanzy/ssh-agent ([Apache-2.0](https://github.com/xanzy/ssh-agent/blob/v0.2.1/LICENSE))
133+
- go.opencensus.io ([Apache-2.0](https://github.com/census-instrumentation/opencensus-go/blob/v0.23.0/LICENSE))
134+
- golang.org/x/crypto ([BSD-3-Clause](https://cs.opensource.google/go/x/crypto/+/5e0467b6:LICENSE))
135+
- golang.org/x/mod/semver ([BSD-3-Clause](https://cs.opensource.google/go/x/mod/+/86c51ed2:LICENSE))
136+
- golang.org/x/net ([BSD-3-Clause](https://cs.opensource.google/go/x/net/+/a158d28d:LICENSE))
137+
- golang.org/x/sys ([BSD-3-Clause](https://cs.opensource.google/go/x/sys/+/8c9f86f7:LICENSE))
138+
- golang.org/x/tools ([BSD-3-Clause](https://cs.opensource.google/go/x/tools/+/v0.1.12:LICENSE))
139+
- gopkg.in/src-d/go-billy.v4 ([Apache-2.0](https://github.com/src-d/go-billy/blob/v4.3.2/LICENSE))
140+
- gopkg.in/src-d/go-git.v4 ([Apache-2.0](https://github.com/src-d/go-git/blob/v4.13.1/LICENSE))
141+
- gopkg.in/warnings.v0 ([BSD-2-Clause](https://github.com/go-warnings/warnings/blob/v0.1.2/LICENSE))
142+
```
143+
144+
This command executes a specified Go template file to generate a report of
145+
licenses. The template file is passed a slice of structs containing license
146+
data:
147+
148+
```go
149+
[]struct {
150+
Name string
151+
LicenseURL string
152+
LicenseName string
153+
}
154+
```
155+
101156
## Save licenses, copyright notices and source code (depending on license type)
102157

103158
```shell
@@ -124,10 +179,16 @@ for licenses considered forbidden.
124179

125180
## Usages
126181

127-
Report usage:
182+
Report usage (default csv output):
183+
184+
```shell
185+
go-licenses report <package> [package...]
186+
```
187+
188+
Report usage (using custom template file):
128189

129190
```shell
130-
go-licenses csv <package> [package...]
191+
go-licenses report <package> [package...] --template=<template_file>
131192
```
132193

133194
Save licenses, copyright notices and source code (depending on license type):
@@ -159,7 +220,7 @@ To read dependencies from packages with
159220
`$GOFLAGS` environment variable.
160221

161222
```shell
162-
$ GOFLAGS="-tags=tools" go-licenses csv google.golang.org/grpc/test/tools
223+
$ GOFLAGS="-tags=tools" go-licenses report google.golang.org/grpc/test/tools
163224
github.com/BurntSushi/toml,https://github.com/BurntSushi/toml/blob/master/COPYING,MIT
164225
google.golang.org/grpc/test/tools,Unknown,Apache-2.0
165226
honnef.co/go/tools/lint,Unknown,BSD-3-Clause
@@ -183,7 +244,7 @@ $ go-licenses check \
183244
```
184245

185246
Note that dependencies from the ignored packages are still resolved and checked.
186-
This flag makes effect to `check`, `csv` and `save` commands.
247+
This flag makes effect to `check`, `report` and `save` commands.
187248

188249
## Warnings and errors
189250

csv.go

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,67 +15,25 @@
1515
package main
1616

1717
import (
18-
"context"
19-
"encoding/csv"
20-
"os"
21-
22-
"github.com/golang/glog"
23-
"github.com/google/go-licenses/licenses"
2418
"github.com/spf13/cobra"
2519
)
2620

2721
var (
28-
csvHelp = "Prints all licenses that apply to one or more Go packages and their dependencies."
22+
csvHelp = "Prints all licenses that apply to one or more Go packages and their dependencies. (Deprecated: use report instead)"
2923
csvCmd = &cobra.Command{
3024
Use: "csv <package> [package...]",
3125
Short: csvHelp,
3226
Long: csvHelp + packageHelp,
3327
Args: cobra.MinimumNArgs(1),
3428
RunE: csvMain,
3529
}
36-
37-
gitRemotes []string
3830
)
3931

4032
func init() {
41-
csvCmd.Flags().StringArrayVar(&gitRemotes, "git_remote", []string{"origin", "upstream"}, "Remote Git repositories to try")
42-
4333
rootCmd.AddCommand(csvCmd)
4434
}
4535

4636
func csvMain(_ *cobra.Command, args []string) error {
47-
writer := csv.NewWriter(os.Stdout)
48-
49-
classifier, err := licenses.NewClassifier(confidenceThreshold)
50-
if err != nil {
51-
return err
52-
}
53-
54-
libs, err := licenses.Libraries(context.Background(), classifier, ignore, args...)
55-
if err != nil {
56-
return err
57-
}
58-
for _, lib := range libs {
59-
licenseURL := "Unknown"
60-
licenseName := "Unknown"
61-
if lib.LicensePath != "" {
62-
name, _, err := classifier.Identify(lib.LicensePath)
63-
if err == nil {
64-
licenseName = name
65-
} else {
66-
glog.Errorf("Error identifying license in %q: %v", lib.LicensePath, err)
67-
}
68-
url, err := lib.FileURL(context.Background(), lib.LicensePath)
69-
if err == nil {
70-
licenseURL = url
71-
} else {
72-
glog.Warningf("Error discovering license URL: %s", err)
73-
}
74-
}
75-
if err := writer.Write([]string{lib.Name(), licenseURL, licenseName}); err != nil {
76-
return err
77-
}
78-
}
79-
writer.Flush()
80-
return writer.Error()
37+
// without a --template flag, reportMain will output CSV
38+
return reportMain(nil, args)
8139
}

e2e_test.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,26 @@ import (
2929

3030
var update = flag.Bool("update", false, "update golden files")
3131

32-
func TestCsvCommandE2E(t *testing.T) {
33-
workdirs := []string{
34-
"testdata/modules/hello01",
35-
"testdata/modules/cli02",
36-
"testdata/modules/vendored03",
37-
"testdata/modules/replace04",
32+
func TestReportCommandE2E(t *testing.T) {
33+
tests := []struct {
34+
workdir string
35+
args []string // additional arguments to pass to report command.
36+
goldenFilePath string
37+
}{
38+
{"testdata/modules/hello01", nil, "licenses.csv"},
39+
{"testdata/modules/cli02", nil, "licenses.csv"},
40+
{"testdata/modules/vendored03", nil, "licenses.csv"},
41+
{"testdata/modules/replace04", nil, "licenses.csv"},
42+
43+
{"testdata/modules/hello01", []string{"--template", "licenses.tpl"}, "licenses.md"},
3844
}
45+
3946
originalWorkDir, err := os.Getwd()
4047
if err != nil {
4148
t.Fatal(err)
4249
}
50+
t.Cleanup(func() { _ = os.Chdir(originalWorkDir) })
51+
4352
// This builds go-licenses CLI to temporary dir.
4453
tempDir, err := ioutil.TempDir("", "")
4554
if err != nil {
@@ -53,9 +62,10 @@ func TestCsvCommandE2E(t *testing.T) {
5362
t.Fatal(err)
5463
}
5564
t.Logf("Built go-licenses binary in %s.", goLicensesPath)
56-
for _, workdir := range workdirs {
57-
t.Run(workdir, func(t *testing.T) {
58-
err := os.Chdir(filepath.Join(originalWorkDir, workdir))
65+
66+
for _, tt := range tests {
67+
t.Run(tt.workdir, func(t *testing.T) {
68+
err := os.Chdir(filepath.Join(originalWorkDir, tt.workdir))
5969
if err != nil {
6070
t.Fatal(err)
6171
}
@@ -64,25 +74,25 @@ func TestCsvCommandE2E(t *testing.T) {
6474
if err != nil {
6575
t.Fatalf("downloading go modules:\n%s", string(log))
6676
}
67-
cmd = exec.Command(goLicensesPath, "csv", ".")
77+
args := append([]string{"report", "."}, tt.args...)
78+
cmd = exec.Command(goLicensesPath, args...)
6879
// Capture stderr to buffer.
6980
var stderr bytes.Buffer
7081
cmd.Stderr = &stderr
71-
t.Logf("%s $ go-licenses csv .", workdir)
82+
t.Logf("%s $ go-licenses csv .", tt.workdir)
7283
output, err := cmd.Output()
7384
if err != nil {
7485
t.Logf("\n=== start of log ===\n%s=== end of log ===\n\n\n", stderr.String())
7586
t.Fatalf("running go-licenses csv: %s. Full log shown above.", err)
7687
}
7788
got := string(output)
78-
goldenFilePath := "licenses.csv"
7989
if *update {
80-
err := ioutil.WriteFile(goldenFilePath, output, 0600)
90+
err := ioutil.WriteFile(tt.goldenFilePath, output, 0600)
8191
if err != nil {
8292
t.Fatalf("writing golden file: %s", err)
8393
}
8494
}
85-
goldenBytes, err := ioutil.ReadFile(goldenFilePath)
95+
goldenBytes, err := ioutil.ReadFile(tt.goldenFilePath)
8696
if err != nil {
8797
if errors.Is(err, os.ErrNotExist) {
8898
t.Fatalf("reading golden file: %s. Create a golden file by running `go test --update .`", err)

report.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright 2019 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"context"
19+
"encoding/csv"
20+
"io/ioutil"
21+
"os"
22+
"text/template"
23+
24+
"github.com/golang/glog"
25+
"github.com/google/go-licenses/licenses"
26+
"github.com/spf13/cobra"
27+
)
28+
29+
var (
30+
reportHelp = "Prints report of all licenses that apply to one or more Go packages and their dependencies."
31+
reportCmd = &cobra.Command{
32+
Use: "report <package> [package...]",
33+
Short: reportHelp,
34+
Long: reportHelp + packageHelp,
35+
Args: cobra.MinimumNArgs(1),
36+
RunE: reportMain,
37+
}
38+
39+
templateFile string
40+
)
41+
42+
func init() {
43+
reportCmd.Flags().StringVar(&templateFile, "template", "", "Custom Go template file to use for report")
44+
45+
rootCmd.AddCommand(reportCmd)
46+
}
47+
48+
type libraryData struct {
49+
Name string
50+
LicenseURL string
51+
LicenseName string
52+
}
53+
54+
func reportMain(_ *cobra.Command, args []string) error {
55+
classifier, err := licenses.NewClassifier(confidenceThreshold)
56+
if err != nil {
57+
return err
58+
}
59+
60+
libs, err := licenses.Libraries(context.Background(), classifier, ignore, args...)
61+
if err != nil {
62+
return err
63+
}
64+
65+
var reportData []libraryData
66+
for _, lib := range libs {
67+
libData := libraryData{
68+
Name: lib.Name(),
69+
LicenseURL: "Unknown",
70+
LicenseName: "Unknown",
71+
}
72+
if lib.LicensePath != "" {
73+
name, _, err := classifier.Identify(lib.LicensePath)
74+
if err == nil {
75+
libData.LicenseName = name
76+
} else {
77+
glog.Errorf("Error identifying license in %q: %v", lib.LicensePath, err)
78+
}
79+
url, err := lib.FileURL(context.Background(), lib.LicensePath)
80+
if err == nil {
81+
libData.LicenseURL = url
82+
} else {
83+
glog.Warningf("Error discovering license URL: %s", err)
84+
}
85+
}
86+
reportData = append(reportData, libData)
87+
}
88+
89+
if templateFile == "" {
90+
return reportCSV(reportData)
91+
} else {
92+
return reportTemplate(reportData)
93+
}
94+
}
95+
96+
func reportCSV(libs []libraryData) error {
97+
writer := csv.NewWriter(os.Stdout)
98+
for _, lib := range libs {
99+
if err := writer.Write([]string{lib.Name, lib.LicenseURL, lib.LicenseName}); err != nil {
100+
return err
101+
}
102+
}
103+
writer.Flush()
104+
return writer.Error()
105+
}
106+
107+
func reportTemplate(libs []libraryData) error {
108+
templateBytes, err := ioutil.ReadFile(templateFile)
109+
if err != nil {
110+
return err
111+
}
112+
tmpl, err := template.New("").Parse(string(templateBytes))
113+
if err != nil {
114+
return err
115+
}
116+
return tmpl.Execute(os.Stdout, libs)
117+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
- github.com/google/go-licenses/testdata/modules/hello01 ([Apache-2.0](https://github.com/google/go-licenses/blob/HEAD/testdata/modules/hello01/LICENSE))

0 commit comments

Comments
 (0)