github.com/cilium/cilium@v1.16.2/tools/spdxconv/main.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"go/ast"
     9  	"go/format"
    10  	"go/parser"
    11  	"go/token"
    12  	"log"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strings"
    17  )
    18  
    19  const (
    20  	usage = `spdxconv is a tool for converting Cilium's copyright and license header
    21  to a SPDX compliant one
    22  
    23  Usage:
    24      spdxconv [package_dir] - provide spdxconv with a absolute path to the go package you'd like to convert.
    25  
    26  `
    27  )
    28  
    29  var (
    30  	// RegexMap maps an identifying regexp expression
    31  	// with the SPDX string that replaces it.
    32  	RegexpMap = map[*regexp.Regexp]string{
    33  		regexp.MustCompile(`(?m)Apache License, Version 2.0`): "// SPDX-License-Identifier: Apache-2.0",
    34  	}
    35  )
    36  
    37  type SPDXConverter struct {
    38  	// the root directory to begin replacing
    39  	// license clauses to spdx clauses
    40  	rootDir string
    41  }
    42  
    43  // Walk will begin walking the tree rooted at
    44  // rootDir.
    45  //
    46  // For each .go file it encounters it will parse
    47  // that file's AST and discover the comments.
    48  //
    49  // If the comments indicate a non spdx license
    50  // it will convert it to one and write the file back
    51  // to disk.
    52  func (s *SPDXConverter) Walk() error {
    53  	err := filepath.WalkDir(s.rootDir, func(path string, d os.DirEntry, e error) error {
    54  		if e != nil {
    55  			return e
    56  		}
    57  		if filepath.Ext(path) != ".go" {
    58  			return nil
    59  		}
    60  		fs := token.NewFileSet()
    61  		astFd, err := parser.ParseFile(fs, path, nil, parser.ParseComments)
    62  		if err != nil {
    63  			return fmt.Errorf("error while parsing %v: %w", path, err)
    64  		}
    65  
    66  		err = s.conv(path, fs, astFd)
    67  		if err != nil {
    68  			return err
    69  		}
    70  
    71  		return nil
    72  	})
    73  	return err
    74  }
    75  
    76  func (s *SPDXConverter) conv(path string, fset *token.FileSet, f *ast.File) error {
    77  	if len(f.Comments) == 0 {
    78  		return nil
    79  	}
    80  	cg := f.Comments[0]
    81  	if !strings.HasPrefix(cg.List[0].Text, "// Copyright") {
    82  		log.Printf("file %v did not start with a Copyright string. skipping...", path)
    83  		return nil
    84  	}
    85  	var spdx string
    86  	for exp, tmp := range RegexpMap {
    87  		if exp.MatchString(cg.Text()) {
    88  			spdx = tmp
    89  		}
    90  	}
    91  	if spdx == "" {
    92  		log.Printf("could not determine license for %v", path)
    93  		return nil
    94  	}
    95  
    96  	copyRight := cg.List[0].Text
    97  	cg.List[0].Text, cg.List[1].Text = spdx, copyRight
    98  	cg.List = cg.List[:2]
    99  
   100  	fd, err := os.OpenFile(path, os.O_TRUNC|os.O_RDWR, 0660)
   101  	if err != nil {
   102  		return fmt.Errorf("failed to open original source file for conversion: %w", err)
   103  	}
   104  	defer fd.Close()
   105  
   106  	err = format.Node(fd, fset, f)
   107  	if err != nil {
   108  		return fmt.Errorf("failed to write converted file %v: %w", path, err)
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  func main() {
   115  	switch {
   116  	case len(os.Args) != 2:
   117  		fmt.Print(usage)
   118  		os.Exit(1)
   119  	case os.Args[1] == "help":
   120  		fmt.Print(usage)
   121  		os.Exit(0)
   122  	}
   123  
   124  	path := os.Args[1]
   125  	dir, err := os.Stat(path)
   126  	if err != nil {
   127  		fmt.Printf("provided path %v could not be opened: %v", path, err)
   128  		os.Exit(1)
   129  	}
   130  	if !dir.IsDir() {
   131  		fmt.Println("provided path is not a directory")
   132  		os.Exit(1)
   133  	}
   134  
   135  	conv := &SPDXConverter{
   136  		rootDir: path,
   137  	}
   138  	if err := conv.Walk(); err != nil {
   139  		log.Fatal(err.Error())
   140  	}
   141  }