glp/projecter/project.go

175 lines
4.2 KiB
Go

package projecter
import (
// stdlib
"bufio"
"log"
"os"
"path/filepath"
"strings"
// local
"go.dev.pztrn.name/glp/configuration"
"go.dev.pztrn.name/glp/parsers"
"go.dev.pztrn.name/glp/structs"
// other
"gopkg.in/src-d/go-license-detector.v3/licensedb"
"gopkg.in/src-d/go-license-detector.v3/licensedb/filer"
)
// Project represents single project (or package) that was passed via
// -pkgs parameter.
type Project struct {
packagePath string
parserName string
flavor string
deps []*structs.Dependency
}
// NewProject creates new project and returns it.
func NewProject(packagePath string) *Project {
p := &Project{}
p.initialize(packagePath)
return p
}
// GetDeps returns list of dependencies for project.
func (p *Project) GetDeps() []*structs.Dependency {
return p.deps
}
// Initializes project.
func (p *Project) initialize(packagePath string) {
p.packagePath = packagePath
// Prepare package path to be used.
// First - replace "~" with actual home directory.
if strings.Contains(p.packagePath, "~") {
homeDir, err := os.UserHomeDir()
if err != nil {
log.Fatalln("Failed to get user's home directory:", err.Error())
}
p.packagePath = strings.Replace(p.packagePath, "~", homeDir, -1)
}
// Get absolute path.
var err error
p.packagePath, err = filepath.Abs(p.packagePath)
if err != nil {
log.Fatalln("Failed to get absolute path for package '"+p.packagePath+":", err.Error())
}
}
// Parses license file for copyrights.
func (p *Project) parseLicenseForCopyrights(licencePath string) []string {
f, err := os.Open(licencePath)
if err != nil {
log.Println("Failed to open license file for reading:", err.Error())
return nil
}
var copyrights []string
// Read file data line by line.
gosum := bufio.NewScanner(f)
gosum.Split(bufio.ScanLines)
for gosum.Scan() {
line := gosum.Text()
if strings.HasPrefix(strings.ToLower(line), "copyright ") && !strings.Contains(strings.ToLower(line), "notice") {
copyrights = append(copyrights, line)
}
}
return copyrights
}
// Starts project parsing.
func (p *Project) process() {
// We should determine project type.
p.parserName, p.flavor = parsers.Detect(p.packagePath)
if p.parserName == "unknown" {
log.Println("Project", p.packagePath, "cannot be parsed with glp")
return
}
// Lets try to get dependencies, their versions and URLs.
deps, err := parsers.GetDependencies(p.parserName, p.flavor, p.packagePath)
if err != nil {
log.Fatalln("Failed to get dependencies:", err.Error())
}
p.deps = deps
// Get licensing information for every dependency.
for _, dep := range p.deps {
// Prepare dependency's things. For now - only check if
// file/directory templates defined and, if not, generate
// them.
dep.VCS.FormatSourcePaths()
depDir, err := filer.FromDirectory(dep.LocalPath)
if err != nil {
log.Println("Failed to prepare directory path for dependency license scan:", err.Error())
continue
}
licenses, err1 := licensedb.Detect(depDir)
if err1 != nil {
log.Println("Failed to detect license for", dep.Name+":", err1.Error())
dep.License.Name = "Unknown"
continue
}
if configuration.Cfg.Log.Debug {
log.Printf("Got licenses result for '%s': %+v\n", dep.Name, licenses)
}
// Get highest ranked license.
var (
licenseFile string
licenseName string
licenseRank float32
)
for name, result := range licenses {
if licenseRank < result.Confidence {
licenseName = name
licenseRank = result.Confidence
for fileName, confidence := range result.Files {
if confidence == licenseRank {
licenseFile = fileName
break
}
}
}
}
if licenseName == "" {
dep.License.Name = "Unknown"
continue
}
log.Printf("Got license for '%s': %s", dep.Name, licenseName)
dep.License.Name = licenseName
// Generate license URL.
urlFormatter := strings.NewReplacer("{dir}", "", "{/dir}", "", "{file}", licenseFile, "{/file}", licenseFile, "#L{line}", "")
dep.License.URL = urlFormatter.Replace(dep.VCS.SourceURLFileTemplate)
// As we should have dependency locally available we should try
// to parse license file to get copyrights.
dep.License.Copyrights = p.parseLicenseForCopyrights(filepath.Join(dep.LocalPath, licenseFile))
}
}