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