github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/utils/nuget/dependencies/assetsjson.go (about)

     1  package dependencies
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"github.com/jfrog/jfrog-client-go/artifactory/buildinfo"
     8  	"github.com/jfrog/jfrog-client-go/utils/errorutils"
     9  	"github.com/jfrog/jfrog-client-go/utils/io/fileutils"
    10  	"github.com/jfrog/jfrog-client-go/utils/log"
    11  	"io/ioutil"
    12  	"path/filepath"
    13  	"strings"
    14  )
    15  
    16  var assetsFilePath = filepath.Join("obj", "project.assets.json")
    17  
    18  // Register project.assets.json extractor
    19  func init() {
    20  	register(&assetsExtractor{})
    21  }
    22  
    23  // project.assets.json dependency extractor
    24  type assetsExtractor struct {
    25  	assets *assets
    26  }
    27  
    28  func (extractor *assetsExtractor) IsCompatible(projectName, projectRoot string) (bool, error) {
    29  	assetsFilePath := filepath.Join(projectRoot, assetsFilePath)
    30  	exists, err := fileutils.IsFileExists(assetsFilePath, false)
    31  	if exists {
    32  		log.Debug("Found", assetsFilePath, "file for project:", projectName)
    33  		return true, err
    34  	}
    35  	return false, err
    36  }
    37  
    38  func (extractor *assetsExtractor) DirectDependencies() ([]string, error) {
    39  	return extractor.assets.getDirectDependencies(), nil
    40  }
    41  
    42  func (extractor *assetsExtractor) AllDependencies() (map[string]*buildinfo.Dependency, error) {
    43  	return extractor.assets.getAllDependencies()
    44  }
    45  
    46  func (extractor *assetsExtractor) ChildrenMap() (map[string][]string, error) {
    47  	return extractor.assets.getChildrenMap(), nil
    48  }
    49  
    50  // Create new assets json extractor.
    51  func (extractor *assetsExtractor) new(projectName, projectRoot string) (Extractor, error) {
    52  	newExtractor := &assetsExtractor{}
    53  	assetsFilePath := filepath.Join(projectRoot, assetsFilePath)
    54  	content, err := ioutil.ReadFile(assetsFilePath)
    55  	if err != nil {
    56  		return nil, errorutils.CheckError(err)
    57  	}
    58  
    59  	assets := &assets{}
    60  	err = json.Unmarshal(content, assets)
    61  	if err != nil {
    62  		return nil, errorutils.CheckError(err)
    63  	}
    64  	newExtractor.assets = assets
    65  	return newExtractor, nil
    66  }
    67  
    68  func (assets *assets) getChildrenMap() map[string][]string {
    69  	dependenciesRelations := map[string][]string{}
    70  	for _, dependencies := range assets.Targets {
    71  		for dependencyId, targetDependencies := range dependencies {
    72  			var transitive []string
    73  			for transitiveName := range targetDependencies.Dependencies {
    74  				transitive = append(transitive, strings.ToLower(transitiveName))
    75  			}
    76  			dependencyName := getDependencyName(dependencyId)
    77  			dependenciesRelations[dependencyName] = transitive
    78  		}
    79  	}
    80  	return dependenciesRelations
    81  }
    82  
    83  func (assets *assets) getDirectDependencies() []string {
    84  	var directDependencies []string
    85  	for _, framework := range assets.Project.Frameworks {
    86  		for dependencyName := range framework.Dependencies {
    87  			directDependencies = append(directDependencies, strings.ToLower(dependencyName))
    88  		}
    89  	}
    90  	return directDependencies
    91  }
    92  
    93  func (assets *assets) getAllDependencies() (map[string]*buildinfo.Dependency, error) {
    94  	dependencies := map[string]*buildinfo.Dependency{}
    95  	packagesPath := assets.Project.Restore.PackagesPath
    96  	for dependencyId, library := range assets.Libraries {
    97  		if library.Type == "project" {
    98  			continue
    99  		}
   100  		nupkgFileName, err := library.getNupkgFileName()
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		nupkgFilePath := filepath.Join(packagesPath, library.Path, nupkgFileName)
   105  		exists, err := fileutils.IsFileExists(nupkgFilePath, false)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		if !exists {
   110  			if assets.isPackagePartOfTargetDependencies(library.Path) {
   111  				log.Warn("The file", nupkgFilePath, "doesn't exist in the NuGet cache directory but it does exist as a target in the assets files. Skipping adding this file to the build info.")
   112  				continue
   113  			}
   114  			return nil, errorutils.CheckError(errors.New("The file " + nupkgFilePath + " doesn't exist in the NuGet cache directory."))
   115  		}
   116  		fileDetails, err := fileutils.GetFileDetails(nupkgFilePath)
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  
   121  		dependencyName := getDependencyName(dependencyId)
   122  		dependencies[dependencyName] = &buildinfo.Dependency{Id: getDependencyIdForBuildInfo(dependencyId), Checksum: &buildinfo.Checksum{Sha1: fileDetails.Checksum.Sha1, Md5: fileDetails.Checksum.Md5}}
   123  	}
   124  
   125  	return dependencies, nil
   126  }
   127  
   128  // If the package is included in the targets section of the assets.json file,
   129  // then this is a .NET dependency that shouldn't be included in the build-info dependencies list
   130  // (it come with the SDK).
   131  // Those files are located in the following path: C:\Program Files\dotnet\sdk\NuGetFallbackFolder
   132  func (assets *assets) isPackagePartOfTargetDependencies(nugetPackageName string) bool {
   133  	for _, dependencies := range assets.Targets {
   134  		for dependencyId := range dependencies {
   135  			// The package names in the targets section of the assets.json file are
   136  			// case insensitive.
   137  			if strings.EqualFold(dependencyId, nugetPackageName) {
   138  				return true
   139  			}
   140  		}
   141  	}
   142  	return false
   143  }
   144  
   145  // Dependencies-id in assets is built in form of: <package-name>/<version>.
   146  // The Build-info format of dependency id is: <package-name>:<version>.
   147  func getDependencyIdForBuildInfo(dependencyAssetId string) string {
   148  	return strings.Replace(dependencyAssetId, "/", ":", 1)
   149  }
   150  
   151  func getDependencyName(dependencyId string) string {
   152  	return strings.ToLower(dependencyId)[0:strings.Index(dependencyId, "/")]
   153  }
   154  
   155  // Assets json objects for unmarshalling
   156  type assets struct {
   157  	Version   int
   158  	Targets   map[string]map[string]targetDependency `json:"targets,omitempty"`
   159  	Libraries map[string]library                     `json:"libraries,omitempty"`
   160  	Project   project                                `json:"project"`
   161  }
   162  
   163  type targetDependency struct {
   164  	Dependencies map[string]string `json:"dependencies,omitempty"` // Transitive dependencies
   165  }
   166  
   167  type library struct {
   168  	Type  string   `json:"type,omitempty"`
   169  	Path  string   `json:"path,omitempty"`
   170  	Files []string `json:"files,omitempty"`
   171  }
   172  
   173  func (library *library) getNupkgFileName() (string, error) {
   174  	for _, fileName := range library.Files {
   175  		if strings.HasSuffix(fileName, "nupkg.sha512") {
   176  			return strings.TrimSuffix(fileName, ".sha512"), nil
   177  		}
   178  	}
   179  	return "", errorutils.CheckError(fmt.Errorf("Could not find nupkg file name for: %s", library.Path))
   180  }
   181  
   182  type project struct {
   183  	Version    string               `json:"version,omitempty"`
   184  	Restore    restore              `json:"restore"`
   185  	Frameworks map[string]framework `json:"frameworks,omitempty"`
   186  }
   187  
   188  type restore struct {
   189  	PackagesPath string `json:"packagesPath"`
   190  }
   191  
   192  type framework struct {
   193  	Dependencies map[string]dependency `json:"dependencies,omitempty"` // Direct dependencies
   194  }
   195  
   196  type dependency struct {
   197  	Target  string `json:"target"`
   198  	Version string `json:"version,omitempty"`
   199  }