Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
245
tools/license_checker/main.go
Normal file
245
tools/license_checker/main.go
Normal file
|
@ -0,0 +1,245 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/text"
|
||||
"golang.org/x/mod/modfile"
|
||||
)
|
||||
|
||||
//go:embed data/spdx_mapping.json
|
||||
var spdxMappingFile []byte
|
||||
|
||||
var debug bool
|
||||
var nameToSPDX map[string]string
|
||||
|
||||
func debugf(format string, v ...any) {
|
||||
if !debug {
|
||||
return
|
||||
}
|
||||
log.Printf("DEBUG: "+format, v...)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var help, verbose bool
|
||||
var threshold float64
|
||||
var whitelistFn, userpkg string
|
||||
|
||||
flag.BoolVar(&debug, "debug", false, "output debugging information")
|
||||
flag.BoolVar(&help, "help", false, "output this help text")
|
||||
flag.BoolVar(&verbose, "verbose", false, "output verbose information instead of just errors")
|
||||
flag.Float64Var(&threshold, "threshold", 0.8, "threshold for license classification")
|
||||
flag.StringVar(&whitelistFn, "whitelist", "", "use the given white-list file for comparison")
|
||||
flag.StringVar(&userpkg, "package", "", "only test the given package (all by default)")
|
||||
flag.Parse()
|
||||
|
||||
if help || flag.NArg() > 1 {
|
||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s [options] [telegraf root dir]\n", os.Args[0])
|
||||
fmt.Fprintf(flag.CommandLine.Output(), "Options:\n")
|
||||
flag.PrintDefaults()
|
||||
fmt.Fprintf(flag.CommandLine.Output(), "\n")
|
||||
fmt.Fprintf(flag.CommandLine.Output(), "Arguments:\n")
|
||||
fmt.Fprintf(flag.CommandLine.Output(), " telegraf root dir (optional)\n")
|
||||
fmt.Fprintf(flag.CommandLine.Output(), " path to the root directory of telegraf (default: .)\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Setup full-name to license SPDX identifier mapping
|
||||
if err := json.Unmarshal(spdxMappingFile, &nameToSPDX); err != nil {
|
||||
log.Fatalf("Unmarshalling license name to SPDX mapping failed: %v", err)
|
||||
}
|
||||
|
||||
// Get required files
|
||||
path := "."
|
||||
if flag.NArg() == 1 {
|
||||
path = flag.Arg(0)
|
||||
}
|
||||
|
||||
moduleFilename := filepath.Join(path, "go.mod")
|
||||
licenseFilename := filepath.Join(path, "docs", "LICENSE_OF_DEPENDENCIES.md")
|
||||
|
||||
var override whitelist
|
||||
if whitelistFn != "" {
|
||||
log.Printf("Reading whitelist file %q...", whitelistFn)
|
||||
if err := override.Parse(whitelistFn); err != nil {
|
||||
log.Fatalf("Reading whitelist failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Reading module file %q...", moduleFilename)
|
||||
modbuf, err := os.ReadFile(moduleFilename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
depModules, err := modfile.Parse(moduleFilename, modbuf, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Parsing modules failed: %f", err)
|
||||
}
|
||||
debugf("found %d required packages", len(depModules.Require))
|
||||
|
||||
dependencies := make(map[string]string)
|
||||
for _, d := range depModules.Require {
|
||||
dependencies[d.Mod.Path] = d.Mod.Version
|
||||
}
|
||||
|
||||
log.Printf("Reading license file %q...", licenseFilename)
|
||||
licensesMarkdown, err := os.ReadFile(licenseFilename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Parse the markdown document
|
||||
parser := goldmark.DefaultParser()
|
||||
root := parser.Parse(text.NewReader(licensesMarkdown))
|
||||
|
||||
// Prepare a line parser
|
||||
lineParser := goldmark.DefaultParser()
|
||||
|
||||
// Collect the licenses
|
||||
// For each list we search for the items and parse them.
|
||||
// Expect a pattern of <package name> <link>.
|
||||
ignored := 0
|
||||
var packageInfos []packageInfo
|
||||
for node := root.FirstChild(); node != nil; node = node.NextSibling() {
|
||||
listNode, ok := node.(*ast.List)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for inode := listNode.FirstChild(); inode != nil; inode = inode.NextSibling() {
|
||||
itemNode, ok := inode.(*ast.ListItem)
|
||||
if !ok || itemNode.ChildCount() != 1 {
|
||||
continue
|
||||
}
|
||||
textNode, ok := itemNode.FirstChild().(*ast.TextBlock)
|
||||
if !ok || textNode.Lines().Len() != 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
lineSegment := textNode.Lines().At(0)
|
||||
line := lineSegment.Value(licensesMarkdown)
|
||||
lineRoot := lineParser.Parse(text.NewReader(line))
|
||||
if lineRoot.ChildCount() != 1 || lineRoot.FirstChild().ChildCount() < 2 {
|
||||
log.Printf("WARN: Ignoring item %q due to wrong count (%d/%d)", string(line), lineRoot.ChildCount(), lineRoot.FirstChild().ChildCount())
|
||||
ignored++
|
||||
continue
|
||||
}
|
||||
|
||||
var name, license, link string
|
||||
for lineElementNode := lineRoot.FirstChild().FirstChild(); lineElementNode != nil; lineElementNode = lineElementNode.NextSibling() {
|
||||
switch v := lineElementNode.(type) {
|
||||
case *ast.Text:
|
||||
name += string(v.Value(line))
|
||||
case *ast.Link:
|
||||
license = string(v.FirstChild().(*ast.Text).Value(line))
|
||||
link = string(v.Destination)
|
||||
default:
|
||||
debugf("ignoring unknown element %T (%v)", v, v)
|
||||
}
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
|
||||
info := packageInfo{
|
||||
name: name,
|
||||
version: dependencies[name],
|
||||
url: strings.TrimSpace(link),
|
||||
license: strings.TrimSpace(license),
|
||||
}
|
||||
info.ToSPDX()
|
||||
if info.name == "" {
|
||||
log.Printf("WARN: Ignoring item %q due to empty package name", string(line))
|
||||
ignored++
|
||||
continue
|
||||
}
|
||||
if info.url == "" {
|
||||
log.Printf("WARN: Ignoring item %q due to empty url name", string(line))
|
||||
ignored++
|
||||
continue
|
||||
}
|
||||
if info.license == "" {
|
||||
log.Printf("WARN: Ignoring item %q due to empty license name", string(line))
|
||||
ignored++
|
||||
continue
|
||||
}
|
||||
debugf("adding %q with license %q (%s) and version %q at %q...", info.name, info.license, info.spdx, info.version, info.url)
|
||||
packageInfos = append(packageInfos, info)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the superset of licenses
|
||||
if debug {
|
||||
licenseSet := make(map[string]bool, len(packageInfos))
|
||||
licenseNames := make([]string, 0, len(packageInfos))
|
||||
for _, info := range packageInfos {
|
||||
if found := licenseSet[info.license]; !found {
|
||||
licenseNames = append(licenseNames, info.license)
|
||||
}
|
||||
licenseSet[info.license] = true
|
||||
}
|
||||
sort.Strings(licenseNames)
|
||||
log.Println("Using licenses:")
|
||||
for _, license := range licenseNames {
|
||||
log.Println(" " + license)
|
||||
}
|
||||
}
|
||||
|
||||
// Check the licenses by matching their text and compare the classification result
|
||||
// with the information provided by the user
|
||||
var succeeded, warn, failed int
|
||||
for _, info := range packageInfos {
|
||||
// Ignore all packages except the ones given by the user (if any)
|
||||
if userpkg != "" && userpkg != info.name {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we got a whitelist entry for the package
|
||||
if ok, found := override.Check(info.name, info.version, info.spdx); found {
|
||||
if ok {
|
||||
log.Printf("OK: \"%s@%s\" (%s) (whitelist)", info.name, info.version, info.license)
|
||||
succeeded++
|
||||
} else {
|
||||
log.Printf("ERR: \"%s@%s\" (%s) %s does not match whitelist", info.name, info.version, info.license, info.spdx)
|
||||
failed++
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Perform a text classification
|
||||
confidence, err := info.Classify()
|
||||
if err != nil {
|
||||
log.Printf("ERR: %q (%s) %v", info.name, info.license, err)
|
||||
failed++
|
||||
continue
|
||||
}
|
||||
if confidence < threshold {
|
||||
log.Printf("WARN: %q (%s) has low matching confidence (%.2f%%)", info.name, info.license, confidence)
|
||||
warn++
|
||||
continue
|
||||
}
|
||||
if verbose {
|
||||
log.Printf("OK: %q (%s) (%.2f%%)", info.name, info.license, confidence)
|
||||
}
|
||||
succeeded++
|
||||
}
|
||||
if verbose {
|
||||
log.Printf("Checked %d licenses (%d ignored lines):", len(packageInfos), ignored)
|
||||
log.Printf(" %d successful", succeeded)
|
||||
log.Printf(" %d low confidence", warn)
|
||||
log.Printf(" %d errors", failed)
|
||||
}
|
||||
|
||||
if failed > 0 {
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue