add support for SPDX license headers
This adds a new "-s" flag that will append an SPDX-License-Identifier line to license headers. If "-s=only" is specified, then only the SPDX identifier will be used. This also changes the "-l" flag to use SPDX identifiers, with mappings to support the legacy "apache", "mit", and "mpl" values. Together with the "-s" flag, this allows SPDX headers for any arbitrary license type to be added to files. Co-authored-by: Bob Callaway <bcallawa@redhat.com>
This commit is contained in:
36
main.go
36
main.go
@@ -49,6 +49,7 @@ Flags:
|
||||
|
||||
var (
|
||||
skipExtensionFlags skipExtensionFlag
|
||||
spdx spdxFlag
|
||||
|
||||
holder = flag.String("c", "Google LLC", "copyright holder")
|
||||
license = flag.String("l", "apache", "license type: apache, bsd, mit, mpl")
|
||||
@@ -64,6 +65,7 @@ func init() {
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
flag.Var(&skipExtensionFlags, "skip", "To skip files to check/add the header file, for example: -skip rb -skip go")
|
||||
flag.Var(&spdx, "s", "Include SPDX identifier in license header. Set -s=only to only include SPDX identifier.")
|
||||
}
|
||||
|
||||
type skipExtensionFlag []string
|
||||
@@ -77,6 +79,29 @@ func (i *skipExtensionFlag) Set(value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// spdxFlag defines the line flag behavior for specifying SPDX support.
|
||||
type spdxFlag string
|
||||
|
||||
const (
|
||||
spdxOff spdxFlag = ""
|
||||
spdxOn spdxFlag = "true" // value set by flag package on bool flag
|
||||
spdxOnly spdxFlag = "only"
|
||||
)
|
||||
|
||||
// IsBoolFlag causes a bare '-s' flag to be set as the string 'true'. This
|
||||
// allows the use of the bare '-s' or setting a string '-s=only'.
|
||||
func (i *spdxFlag) IsBoolFlag() bool { return true }
|
||||
func (i *spdxFlag) String() string { return string(*i) }
|
||||
|
||||
func (i *spdxFlag) Set(value string) error {
|
||||
v := spdxFlag(value)
|
||||
if v != spdxOn && v != spdxOnly {
|
||||
return fmt.Errorf("error: flag 's' expects '%v' or '%v'", spdxOn, spdxOnly)
|
||||
}
|
||||
*i = v
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if flag.NArg() == 0 {
|
||||
@@ -84,12 +109,18 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// map legacy license values
|
||||
if t, ok := legacyLicenseTypes[*license]; ok {
|
||||
*license = t
|
||||
}
|
||||
|
||||
data := licenseData{
|
||||
Year: *year,
|
||||
Holder: *holder,
|
||||
SPDXID: *license,
|
||||
}
|
||||
|
||||
tpl, err := fetchTemplate(*license, *licensef)
|
||||
tpl, err := fetchTemplate(*license, *licensef, spdx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -300,5 +331,6 @@ func hasLicense(b []byte) bool {
|
||||
n = len(b)
|
||||
}
|
||||
return bytes.Contains(bytes.ToLower(b[:n]), []byte("copyright")) ||
|
||||
bytes.Contains(bytes.ToLower(b[:n]), []byte("mozilla public"))
|
||||
bytes.Contains(bytes.ToLower(b[:n]), []byte("mozilla public")) ||
|
||||
bytes.Contains(bytes.ToLower(b[:n]), []byte("SPDX-License-Identifier"))
|
||||
}
|
||||
|
||||
38
tmpl.go
38
tmpl.go
@@ -25,25 +25,36 @@ import (
|
||||
)
|
||||
|
||||
var licenseTemplate = map[string]string{
|
||||
"apache": tmplApache,
|
||||
"mit": tmplMIT,
|
||||
"bsd": tmplBSD,
|
||||
"mpl": tmplMPL,
|
||||
"Apache-2.0": tmplApache,
|
||||
"MIT": tmplMIT,
|
||||
"bsd": tmplBSD,
|
||||
"MPL-2.0": tmplMPL,
|
||||
}
|
||||
|
||||
// maintain backwards compatibility by mapping legacy license types to their
|
||||
// SPDX equivalents.
|
||||
var legacyLicenseTypes = map[string]string{
|
||||
"apache": "Apache-2.0",
|
||||
"mit": "MIT",
|
||||
"mpl": "MPL-2.0",
|
||||
}
|
||||
|
||||
// licenseData specifies the data used to fill out a license template.
|
||||
type licenseData struct {
|
||||
Year string // Copyright year(s).
|
||||
Holder string // Name of the copyright holder.
|
||||
SPDXID string // SPDX Identifier
|
||||
}
|
||||
|
||||
// fetchTemplate returns the license template for the specified license and
|
||||
// optional templateFile. If templateFile is provided, the license is read
|
||||
// from the specified file. Otherwise, a template is loaded for the specified
|
||||
// license, if recognized.
|
||||
func fetchTemplate(license string, templateFile string) (string, error) {
|
||||
func fetchTemplate(license string, templateFile string, spdx spdxFlag) (string, error) {
|
||||
var t string
|
||||
if templateFile != "" {
|
||||
if spdx == spdxOnly {
|
||||
t = tmplSPDX
|
||||
} else if templateFile != "" {
|
||||
d, err := ioutil.ReadFile(templateFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("license file: %w", err)
|
||||
@@ -53,7 +64,15 @@ func fetchTemplate(license string, templateFile string) (string, error) {
|
||||
} else {
|
||||
t = licenseTemplate[license]
|
||||
if t == "" {
|
||||
return "", fmt.Errorf("unknown license: %q", license)
|
||||
if spdx == spdxOn {
|
||||
// unknown license, but SPDX headers requested
|
||||
t = tmplSPDX
|
||||
} else {
|
||||
return "", fmt.Errorf("unknown license: %q. Include the '-s' flag to request SPDX style headers using this license.", license)
|
||||
}
|
||||
} else if spdx == spdxOn {
|
||||
// append spdx headers to recognized license
|
||||
t = t + spdxSuffix
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,3 +141,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`
|
||||
const tmplMPL = `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 https://mozilla.org/MPL/2.0/.`
|
||||
|
||||
const tmplSPDX = `{{ if and .Year .Holder }}Copyright {{.Year}} {{.Holder}}
|
||||
{{ end }}SPDX-License-Identifier: {{.SPDXID}}`
|
||||
|
||||
const spdxSuffix = "\n\nSPDX-License-Identifier: {{.SPDXID}}"
|
||||
|
||||
58
tmpl_test.go
58
tmpl_test.go
@@ -31,16 +31,19 @@ func init() {
|
||||
|
||||
func TestFetchTemplate(t *testing.T) {
|
||||
tests := []struct {
|
||||
description string // test case description
|
||||
license string // license passed to fetchTemplate
|
||||
templateFile string // templatefile passed to fetchTemplate
|
||||
wantTemplate string // expected returned template
|
||||
wantErr error // expected returned error
|
||||
description string // test case description
|
||||
license string // license passed to fetchTemplate
|
||||
templateFile string // templatefile passed to fetchTemplate
|
||||
spdx spdxFlag // spdx value passed to fetchTemplate
|
||||
wantTemplate string // expected returned template
|
||||
wantErr error // expected returned error
|
||||
}{
|
||||
// custom template files
|
||||
{
|
||||
"non-existant template file",
|
||||
"",
|
||||
"/does/not/exist",
|
||||
spdxOff,
|
||||
"",
|
||||
os.ErrNotExist,
|
||||
},
|
||||
@@ -48,27 +51,34 @@ func TestFetchTemplate(t *testing.T) {
|
||||
"custom template file",
|
||||
"",
|
||||
"testdata/custom.tpl",
|
||||
spdxOff,
|
||||
"Copyright {{.Year}} {{.Holder}}\n\nCustom License Template\n",
|
||||
nil,
|
||||
},
|
||||
|
||||
{
|
||||
"unknown license",
|
||||
"unknown",
|
||||
"",
|
||||
spdxOff,
|
||||
"",
|
||||
errors.New(`unknown license: "unknown"`),
|
||||
errors.New(`unknown license: "unknown". Include the '-s' flag to request SPDX style headers using this license.`),
|
||||
},
|
||||
|
||||
// pre-defined license templates, no SPDX
|
||||
{
|
||||
"apache license template",
|
||||
"apache",
|
||||
"Apache-2.0",
|
||||
"",
|
||||
spdxOff,
|
||||
tmplApache,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"mit license template",
|
||||
"mit",
|
||||
"MIT",
|
||||
"",
|
||||
spdxOff,
|
||||
tmplMIT,
|
||||
nil,
|
||||
},
|
||||
@@ -76,21 +86,49 @@ func TestFetchTemplate(t *testing.T) {
|
||||
"bsd license template",
|
||||
"bsd",
|
||||
"",
|
||||
spdxOff,
|
||||
tmplBSD,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"mpl license template",
|
||||
"mpl",
|
||||
"MPL-2.0",
|
||||
"",
|
||||
spdxOff,
|
||||
tmplMPL,
|
||||
nil,
|
||||
},
|
||||
|
||||
// SPDX variants
|
||||
{
|
||||
"apache license template with SPDX added",
|
||||
"Apache-2.0",
|
||||
"",
|
||||
spdxOn,
|
||||
tmplApache + spdxSuffix,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"apache license template with SPDX only",
|
||||
"Apache-2.0",
|
||||
"",
|
||||
spdxOnly,
|
||||
tmplSPDX,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"unknown license with SPDX only",
|
||||
"unknown",
|
||||
"",
|
||||
spdxOnly,
|
||||
tmplSPDX,
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
tpl, err := fetchTemplate(tt.license, tt.templateFile)
|
||||
tpl, err := fetchTemplate(tt.license, tt.templateFile, tt.spdx)
|
||||
if tt.wantErr != nil && (err == nil || (!errors.Is(err, tt.wantErr) && err.Error() != tt.wantErr.Error())) {
|
||||
t.Fatalf("fetchTemplate(%q, %q) returned error: %#v, want %#v", tt.license, tt.templateFile, err, tt.wantErr)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user