From 4e9a94f689148eaab9d14a6d9bd89bb0a08ea2c3 Mon Sep 17 00:00:00 2001 From: ston1th Date: Mon, 5 Sep 2016 22:23:43 +0200 Subject: [PATCH] initial commit --- .gitignore | 1 + LICENSE | 24 ++++++++++ Makefile | 26 +++++++++++ README.md | 5 ++ main.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 main.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b03cca6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +golic diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9e52d9b --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2016 Marius Schellenberger +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of the authors and/or contributors may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ston1th BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5277224 --- /dev/null +++ b/Makefile @@ -0,0 +1,26 @@ +CC=go +BUILD=build -v +GCFLAGS=--gcflags '-e' +LDFLAGS=--ldflags '-s -w' +PROGRAM=golic + +all: $(PROGRAM) + +$(PROGRAM): codeqa + CGO_ENABLED=0 $(CC) $(BUILD) $(GCFLAGS) $(LDFLAGS) + +clean: + $(CC) clean -x + +codeqa: gofmt golint misspell + +gofmt: + gofmt -w . + +golint: + $(GOPATH)/bin/golint ./... + +misspell: + $(GOPATH)/bin/misspell **/* + +.PHONY: clean codeqa gofmt golint misspell diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f62ddd --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# golic + +golic is a simple program to generate a third-party licenses file. + +golic uses Godep and the `vendor/` directory as its source of LICENSE files. diff --git a/main.go b/main.go new file mode 100644 index 0000000..a8ab2fe --- /dev/null +++ b/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + "text/template" +) + +type deps struct { + ImportPath string +} + +type godep struct { + Deps []deps +} + +type license struct { + Path string + URL string + Text string +} + +const ( + markdown = `# Third Party Licenses +{{range $item := .}} +## [{{$item.Path}}]({{$item.URL}}) + +{{$item.Text}} +{{end}}` + text = `Third Party Licenses +{{range $item := .}} +{{$item.Path}} +{{$item.URL}} + +{{$item.Text}} +{{end}}` +) + +var ( + md bool + file string + lics []license + errFound = errors.New("file found") + licre = regexp.MustCompile("([Ll][Ii][Cc][Ee][Nn][Ss][Ee].*)") + pathre = regexp.MustCompile("vendor/(.*/(.*/.*))/") +) + +func init() { + flag.BoolVar(&md, "md", false, "enable markdown output") + flag.StringVar(&file, "f", "third_party_licenses.txt", "output file") +} + +func trim(s string) string { + if strings.Count(s, "/") <= 2 { + return s + } + return trim(s[:strings.LastIndex(s, "/")]) +} + +func main() { + flag.Parse() + var ( + tmp *template.Template + gd godep + ) + if md { + tmp = template.Must(template.New("").Parse(markdown)) + } else { + tmp = template.Must(template.New("").Parse(text)) + } + godeps, err := ioutil.ReadFile("./Godeps/Godeps.json") + if err != nil { + exit(err) + } + err = json.Unmarshal(godeps, &gd) + if err != nil { + exit(err) + } + paths := make(map[string]struct{}) + for _, d := range gd.Deps { + path := trim(d.ImportPath) + if _, ok := paths[path]; ok { + continue + } + err := filepath.Walk(filepath.Join("vendor", path), walker) + if err != nil && err != errFound { + fmt.Fprintln(os.Stderr, err) + } + paths[path] = struct{}{} + } + output, err := os.Create(file) + if err != nil { + exit(err) + } + if err := tmp.Execute(output, lics); err != nil { + output.Close() + exit(err) + } +} + +func walker(path string, info os.FileInfo, err error) error { + if info == nil { + return nil + } + if info.IsDir() { + return nil + } + if !licre.MatchString(info.Name()) { + return nil + } + text, err := ioutil.ReadFile(path) + if err != nil { + return err + } + match := pathre.FindAllStringSubmatch(path, -1) + lics = append(lics, license{ + Path: match[0][2], + URL: match[0][1], + Text: string(text), + }) + return errFound +} + +func exit(err error) { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) +}