1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package main
19
20 import (
21 "fmt"
22 "io/ioutil"
23 "os"
24 "os/exec"
25 "path/filepath"
26 "strings"
27 )
28
29 const (
30 inputDir = "temp-vendor"
31 thirdPartyNoticeDir = "THIRD_PARTY_NOTICES"
32 mirroredLibrarySourceDir = "MIRRORED_LIBRARY_SOURCE"
33 dirMode = 0700
34 fileMode = 0600
35 )
36
37 func main() {
38 var files []string
39 os.RemoveAll(thirdPartyNoticeDir)
40 os.RemoveAll(mirroredLibrarySourceDir)
41
42
43 err := filepath.Walk(inputDir, func(path string, info os.FileInfo, err error) error {
44 if !strings.Contains(path, "LICENSE") && !strings.Contains(path, "LICENCE") {
45 return nil
46 }
47 files = append(files, path)
48 return nil
49 })
50 if err != nil {
51 fmt.Printf("error walking vendor directory: %v\n", err)
52 os.Exit(1)
53 }
54
55 for _, file := range files {
56 licensePath := strings.TrimLeft(file, "temp-vendor/")
57 repo, licenseFilename := splitLicensePath(licensePath)
58 licenseURL := repoToLicenseURL(repo, licenseFilename)
59 fmt.Println(licenseURL)
60
61 outputFilename := thirdPartyNoticeDir + "/" + licensePath
62 outputFileDir := thirdPartyNoticeDir + "/" + repo
63 input, err := ioutil.ReadFile(file)
64 if err != nil {
65 fmt.Println(err)
66 os.Exit(1)
67 }
68 if err := os.MkdirAll(outputFileDir, dirMode); err != nil {
69 fmt.Printf("error creating output directory '%v': %v\n", outputFileDir, err)
70 os.Exit(1)
71 }
72
73
74 if err := ioutil.WriteFile(outputFilename, input, fileMode); err != nil {
75 fmt.Println(err)
76 os.Exit(1)
77 }
78
79 if licenseRequiresSourceCodeMirroring(input) {
80 fmt.Printf("REQUIRES SOURCE MIRRORING: %v\n", file)
81 outputSourceDir := mirroredLibrarySourceDir + "/" + repo
82
83 if err := os.MkdirAll(outputSourceDir, dirMode); err != nil {
84 fmt.Printf("error creating output directory '%v': %v\n", outputFileDir, err)
85 os.Exit(1)
86 }
87
88 os.Remove(outputSourceDir)
89
90 sourceDir := "temp-vendor/" + repo
91 cmd := exec.Command("cp", "-r", sourceDir, outputSourceDir)
92 if output, err := cmd.CombinedOutput(); err != nil {
93 fmt.Printf("error copying source code for '%v': %v", sourceDir, string(output))
94 os.Exit(1)
95 }
96 }
97 }
98 }
99
100 func licenseRequiresSourceCodeMirroring(licenseText []byte) bool {
101 normalizedText := strings.ToLower(string(licenseText))
102 licenseRequiresSourceCodeMirroring := map[string]bool{
103 "mozilla public license": true,
104 "common development and distribution license": true,
105 "eclipse public license": true,
106 "gnu general public license": true,
107 "lesser general public license": true,
108 }
109
110 for licenseType := range licenseRequiresSourceCodeMirroring {
111 if strings.Contains(normalizedText, licenseType) {
112 return true
113 }
114 }
115 return false
116 }
117
118 func splitLicensePath(path string) (repo string, licenseFilename string) {
119 splitPath := strings.Split(path, "/")
120 repo = strings.Join(splitPath[:len(splitPath)-1], "/")
121 licenseFilename = splitPath[len(splitPath)-1]
122 return repo, licenseFilename
123 }
124
125 func repoToLicenseURL(repo string, licenseFilename string) string {
126 if manualLicenseURLMapping[repo] != "" {
127 return manualLicenseURLMapping[repo]
128 }
129 domain, repoRoot, subrepoPath := splitRepo(repo)
130
131 licensePathInRepo := licenseFilename
132 if subrepoPath != "" {
133 licensePathInRepo = strings.Join([]string{subrepoPath, licenseFilename}, "/")
134 }
135
136
137 switch domain {
138 case "cloud.google.com":
139 splitRepoRoot := strings.Split(repoRoot, "/")
140 if len(splitRepoRoot) == 2 && splitRepoRoot[0] == "go" {
141 return fmt.Sprintf("https://github.com/googleapis/google-cloud-go/blob/master/LICENSE")
142 } else {
143 panic(fmt.Sprintf("unrecognized repo under cloud.google.com: %v", repoRoot))
144 }
145 case "sigs.k8s.io":
146 return fmt.Sprintf("https://github.com/kubernetes-sigs/%v/blob/master/%v", repoRoot, licensePathInRepo)
147 case "github.com":
148 return fmt.Sprintf("https://github.com/%v/blob/master/%v", repoRoot, licensePathInRepo)
149 case "golang.org":
150 if !strings.HasPrefix(repoRoot, "x/") {
151 panic(fmt.Sprintf("unhandled domain for repo %v", repo))
152 }
153 newRepoRoot := strings.TrimLeft(repoRoot, "x/")
154 if newRepoRoot == "tools" && strings.Contains(licensePathInRepo, "third_party") {
155
156 return fmt.Sprintf("https://github.com/golang/tools/blob/7414d4c1f71cec71978b1aec0539171a2e42d230/%v", licensePathInRepo)
157 } else {
158 return fmt.Sprintf("https://github.com/golang/%v/blob/master/%v", newRepoRoot, licensePathInRepo)
159 }
160 case "k8s.io":
161 return fmt.Sprintf("https://github.com/kubernetes/%v/blob/master/%v", repoRoot, licensePathInRepo)
162 case "go.uber.org":
163 return fmt.Sprintf("https://github.com/uber-go/%v/blob/master/%v", repoRoot, licensePathInRepo)
164 case "gopkg.in":
165 switch repoRoot {
166 case "yaml.v2":
167 return fmt.Sprintf("https://github.com/go-yaml/yaml/blob/v2.2.2/%v", licensePathInRepo)
168 case "yaml.v3":
169 return fmt.Sprintf("https://github.com/go-yaml/yaml/blob/v3/%v", licensePathInRepo)
170 default:
171 panic(fmt.Sprintf("unhandled domain for repo %v", repo))
172 }
173 case "go.opencensus.io":
174 return fmt.Sprintf("https://github.com/census-instrumentation/opencensus-go/blob/master/%v", licensePathInRepo)
175 case "honnef.co":
176 return fmt.Sprintf("https://github.com/dominikh/go-tools/blob/master/%v", licensePathInRepo)
177 default:
178 panic(fmt.Sprintf("unhandled domain for repo %v", repo))
179 }
180 }
181
182 func splitRepo(repo string) (domain string, repoRoot string, subrepoPath string) {
183 splitRepo := strings.Split(repo, "/")
184 domain = splitRepo[0]
185 if len(splitRepo) == 2 {
186 repoRoot = splitRepo[1]
187 } else if len(splitRepo) > 2 {
188 repoRoot = strings.Join(splitRepo[1:3], "/")
189 }
190 subrepoPath = ""
191 if len(splitRepo) > 3 {
192 subrepoPath = strings.Join(splitRepo[3:], "/")
193 }
194 return domain, repoRoot, subrepoPath
195 }
196
197 var manualLicenseURLMapping = map[string]string{
198 "bitbucket.org/creachadair/stringset": "https://bitbucket.org/creachadair/stringset/src/master/LICENSE",
199 "cloud.google.com/go": "https://github.com/googleapis/google-cloud-go/blob/master/LICENSE",
200 "contrib.go.opencensus.io/exporter/prometheus": "https://github.com/census-ecosystem/opencensus-go-exporter-prometheus/blob/master/LICENSE",
201 "go.starlark.net": "https://github.com/google/starlark-go/blob/master/LICENSE",
202 "gomodules.xyz/jsonpatch/v2": "https://https://github.com/gomodules/jsonpatch/blob/master/LICENSE",
203 "google.golang.org/api": "https://github.com/googleapis/google-api-go-client/blob/master/LICENSE",
204 "google.golang.org/api/googleapi/internal/uritemplates": "https://github.com/googleapis/google-api-go-client/blob/master/googleapi/internal/uritemplates/LICENSE",
205 "google.golang.org/api/internal/third_party/uritemplates": "https://github.com/googleapis/google-api-go-client/blob/master/internal/third_party/uritemplates/LICENSE",
206 "google.golang.org/appengine": "https://github.com/golang/appengine/blob/master/LICENSE",
207 "google.golang.org/genproto": "https://github.com/google/go-genproto/blob/master/LICENSE",
208 "google.golang.org/grpc": "https://github.com/grpc/grpc-go/blob/master/LICENSE",
209 "google.golang.org/protobuf": "https://github.com/protocolbuffers/protobuf-go/blob/master/LICENSE",
210 "gopkg.in/fsnotify.v1": "https://github.com/fsnotify/fsnotify/blob/master/LICENSE",
211 "gopkg.in/inf.v0": "https://github.com/go-inf/inf/blob/master/LICENSE",
212 "gopkg.in/tomb.v1": "https://github.com/go-tomb/tomb/blob/v1/LICENSE",
213 "gopkg.in/vmihailenco/msgpack.v4": "https://github.com/vmihailenco/msgpack/blob/master/LICENSE",
214 "gopkg.in/warnings.v0": "https://github.com/go-warnings/warnings/blob/master/LICENSE",
215 "honnef.co/go/tools": "https://github.com/dominikh/go-tools/blob/master/LICENSE",
216 "honnef.co/go/tools/lint": "https://github.com/dominikh/go-tools/blob/master/lint/LICENSE",
217 "honnef.co/go/tools/ssa": "https://github.com/dominikh/go-tools/blob/master/ssa/LICENSE",
218 }
219
View as plain text