blob: e2613d215c0e84fa4eb6ab2d734a7a4d09152037 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 deps
import (
"encoding/json"
"os"
"os/exec"
"path/filepath"
"regexp"
"github.com/apache/skywalking-eyes/internal/logger"
"github.com/apache/skywalking-eyes/pkg/license"
)
type CargoMetadata struct {
Packages []CargoPackage `json:"packages"`
}
type CargoPackage struct {
Name string `json:"name"`
Version string `json:"version"`
License string `json:"license"`
LicenseFile string `json:"license_file"`
ManifestPath string `json:"manifest_path"`
}
type CargoTomlResolver struct {
Resolver
}
func (resolver *CargoTomlResolver) CanResolve(file string) bool {
base := filepath.Base(file)
logger.Log.Debugln("Base name:", base)
return base == "Cargo.toml"
}
// Resolve resolves licenses of all dependencies declared in the Cargo.toml file.
func (resolver *CargoTomlResolver) Resolve(cargoTomlFile string, config *ConfigDeps, report *Report) error {
dir := filepath.Dir(cargoTomlFile)
download := exec.Command("cargo", "fetch")
logger.Log.Debugf("Run command: %v, please wait", download.String())
download.Stdout = os.Stdout
download.Stderr = os.Stderr
download.Dir = dir
if err := download.Run(); err != nil {
return err
}
cmd := exec.Command("cargo", "metadata", "--format-version=1", "--all-features")
cmd.Dir = dir
output, err := cmd.Output()
if err != nil {
return err
}
var metadata CargoMetadata
if err := json.Unmarshal(output, &metadata); err != nil {
return err
}
logger.Log.Debugln("Package size:", len(metadata.Packages))
return resolver.ResolvePackages(metadata.Packages, config, report)
}
// ResolvePackages resolves the licenses of the given packages.
func (resolver *CargoTomlResolver) ResolvePackages(packages []CargoPackage, config *ConfigDeps, report *Report) error {
for i := range packages {
pkg := packages[i]
if config.IsExcluded(pkg.Name, pkg.Version) {
continue
}
if l, ok := config.GetUserConfiguredLicense(pkg.Name, pkg.Version); ok {
report.Resolve(&Result{
Dependency: pkg.Name,
LicenseSpdxID: l,
Version: pkg.Version,
})
continue
}
err := resolver.ResolvePackageLicense(config, &pkg, report)
if err != nil {
logger.Log.Warnf("Failed to resolve the license of <%s@%s>: %v\n", pkg.Name, pkg.Version, err)
report.Skip(&Result{
Dependency: pkg.Name,
LicenseSpdxID: Unknown,
Version: pkg.Version,
})
}
}
return nil
}
var cargoPossibleLicenseFileName = regexp.MustCompile(`(?i)^LICENSE|LICENCE(\.txt)?|LICENSE-.+|COPYING(\.txt)?$`)
// ResolvePackageLicense resolve the package license.
// The CargoPackage.LicenseFile is generally used for non-standard licenses and is ignored now.
func (resolver *CargoTomlResolver) ResolvePackageLicense(config *ConfigDeps, pkg *CargoPackage, report *Report) error {
dir := filepath.Dir(pkg.ManifestPath)
logger.Log.Debugf("Directory of %+v is %+v", pkg.Name, dir)
files, err := os.ReadDir(dir)
if err != nil {
return nil
}
var licenseFilePath string
var licenseContent []byte
licenseID := pkg.License
for _, info := range files {
if !cargoPossibleLicenseFileName.MatchString(info.Name()) {
continue
}
licenseFilePath = filepath.Join(dir, info.Name())
licenseContent, err = os.ReadFile(licenseFilePath)
if err != nil {
return err
}
break
}
if licenseID == "" { // If pkg.License is empty, identify the license ID from the license file content
if licenseID, err = license.Identify(string(licenseContent), config.Threshold); err != nil {
return err
}
}
report.Resolve(&Result{
Dependency: pkg.Name,
LicenseFilePath: licenseFilePath,
LicenseContent: string(licenseContent),
LicenseSpdxID: licenseID,
Version: pkg.Version,
})
return nil
}