git-sync/vendor/github.com/google/go-licenses/save.go

147 lines
4.4 KiB
Go

// Copyright 2019 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/golang/glog"
"github.com/google/go-licenses/licenses"
"github.com/otiai10/copy"
"github.com/spf13/cobra"
)
var (
saveCmd = &cobra.Command{
Use: "save <package>",
Short: "Saves licenses, copyright notices and source code, as required by a Go package's dependencies, to a directory.",
Args: cobra.MinimumNArgs(1),
RunE: saveMain,
}
noticeRegexp = regexp.MustCompile(`^NOTICE(\.(txt|md))?$`)
// savePath is where the output of the command is written to.
savePath string
// overwriteSavePath controls behaviour when the directory indicated by savePath already exists.
// If true, the directory will be replaced. If false, the command will fail.
overwriteSavePath bool
)
func init() {
saveCmd.Flags().StringVar(&savePath, "save_path", "", "Directory into which files should be saved that are required by license terms")
if err := saveCmd.MarkFlagRequired("save_path"); err != nil {
glog.Fatal(err)
}
if err := saveCmd.MarkFlagFilename("save_path"); err != nil {
glog.Fatal(err)
}
saveCmd.Flags().BoolVar(&overwriteSavePath, "force", false, "Delete the destination directory if it already exists.")
rootCmd.AddCommand(saveCmd)
}
func saveMain(_ *cobra.Command, args []string) error {
if overwriteSavePath {
if err := os.RemoveAll(savePath); err != nil {
return err
}
}
// Check that the save path doesn't exist, otherwise it'd end up with a mix of
// existing files and the output of this command.
if d, err := os.Open(savePath); err == nil {
d.Close()
return fmt.Errorf("%s already exists", savePath)
} else if !os.IsNotExist(err) {
return err
}
classifier, err := licenses.NewClassifier(confidenceThreshold)
if err != nil {
return err
}
libs, err := licenses.Libraries(context.Background(), classifier, args...)
if err != nil {
return err
}
libsWithBadLicenses := make(map[licenses.Type][]*licenses.Library)
for _, lib := range libs {
libSaveDir := filepath.Join(savePath, unvendor(lib.Name()))
// Detect what type of license this library has and fulfill its requirements, e.g. copy license, copyright notice, source code, etc.
_, licenseType, err := classifier.Identify(lib.LicensePath)
if err != nil {
return err
}
switch licenseType {
case licenses.Restricted, licenses.Reciprocal:
// Copy the entire source directory for the library.
libDir := filepath.Dir(lib.LicensePath)
if err := copySrc(libDir, libSaveDir); err != nil {
return err
}
case licenses.Notice, licenses.Permissive, licenses.Unencumbered:
// Just copy the license and copyright notice.
if err := copyNotices(lib.LicensePath, libSaveDir); err != nil {
return err
}
default:
libsWithBadLicenses[licenseType] = append(libsWithBadLicenses[licenseType], lib)
}
}
if len(libsWithBadLicenses) > 0 {
return fmt.Errorf("one or more libraries have an incompatible/unknown license: %q", libsWithBadLicenses)
}
return nil
}
func copySrc(src, dest string) error {
// Skip the .git directory for copying, if it exists, since we don't want to save the user's
// local Git config along with the source code.
opt := copy.Options{Skip: func(src string) (bool, error) { return strings.HasSuffix(src, ".git"), nil }}
if err := copy.Copy(src, dest, opt); err != nil {
return err
}
return nil
}
func copyNotices(licensePath, dest string) error {
if err := copy.Copy(licensePath, filepath.Join(dest, filepath.Base(licensePath))); err != nil {
return err
}
src := filepath.Dir(licensePath)
files, err := ioutil.ReadDir(src)
if err != nil {
return err
}
for _, f := range files {
if fName := f.Name(); !f.IsDir() && noticeRegexp.MatchString(fName) {
if err := copy.Copy(filepath.Join(src, fName), filepath.Join(dest, fName)); err != nil {
return err
}
}
}
return nil
}