github.com/jfrog/jfrog-cli-core@v1.12.1/artifactory/utils/dotnet/dependencies/assetsjson.go (about)

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