import { ComponentDetails } from 'jfrog-client-js' ; import * as Collections from 'typescript-collections' ; import * as vscode from 'vscode' ; import { GeneralInfo } from '../../../types/generalInfo' ; import { GoUtils } from '../../../utils/goUtils' ; import { ScanUtils } from '../../../utils/scanUtils' ; import { TreesManager } from '../../treesManager' ; import { DependenciesTreeNode } from '../dependenciesTreeNode' ; import { RootNode } from './rootTree' ; export class GoTreeNode extends RootNode { private static readonly COMPONENT_PREFIX : string = 'go://' ; constructor ( workspaceFolder , private _componentsToScan , private _treesManager , parent ? ) { super ( workspaceFolder , parent ) ; } public async refreshDependencies ( quickScan ) { let goModGraph = [ ] ; let goList = [ ] ; let rootPackageName = '' ; try { rootPackageName = this . getRootPackageName ( ) ; goModGraph = this . runGoModGraph ( ) ; goList = this . runGoList ( ) ; } catch ( error ) { this . _treesManager . logManager . logError ( < any > error , ! quickScan ) ; this . label = this . workspaceFolder + ' [Not installed]' ; this . generalInfo = new GeneralInfo ( this . label , '' , [ ] , this . workspaceFolder , GoUtils . PKG_TYPE ) ; return ; } this . generalInfo = new GeneralInfo ( rootPackageName , '' , [ 'None' ] , this . workspaceFolder , GoUtils . PKG_TYPE ) ; this . label = rootPackageName ; if ( goModGraph . length === 0 ) { return ; } let dependenciesMap = this . buildDependenciesMapAndDirectDeps ( goModGraph , goList ) ; this . children . forEach ( child => this . populateDependenciesTree ( dependenciesMap , child , quickScan ) ) ; } returns private getRootPackageName ( ) { return ScanUtils . executeCmd ( 'go list -m' , this . workspaceFolder ) . toString ( ) . trim ( ) ; } returns private runGoModGraph ( ) { let goModGraphOutput = ScanUtils . executeCmd ( 'go mod graph' , this . workspaceFolder ) . toString ( ) . split ( /\s+/ ) ; goModGraphOutput . pop ( ) ; let results = [ ] ; for ( let i = 0 ; i < goModGraphOutput . length ; i += 2 ) { results . push ( new PackageDependencyPair ( goModGraphOutput [ i ] , goModGraphOutput [ i + 1 ] ) ) ; } return results ; } returns private runGoList ( ) { return ScanUtils . executeCmd ( `go list -f "{{with .Module}}{{.Path}} {{.Version}}{{end}}" all` , this . workspaceFolder ) . toString ( ) . split ( /\n/ ) ; } private buildDependenciesMapAndDirectDeps ( goModGraph , goList ) { let goModGraphIndex = 0 ; let directDependenciesGeneralInfos = [ ] ; for ( ; goModGraphIndex < goModGraph . length && ! goModGraph [ goModGraphIndex ] . package . includes ( '@' ) ; goModGraphIndex ++ ) { let nameVersionTuple = this . getNameVersionTuple ( goModGraph [ goModGraphIndex ] . dependency ) ; directDependenciesGeneralInfos . push ( new GeneralInfo ( nameVersionTuple [ 0 ] , nameVersionTuple [ 1 ] , [ 'None' ] , '' , GoUtils . PKG_TYPE ) ) ; } let goListPackages = new Set < string > ( ) ; goList . forEach ( ( dependency ) => { goListPackages . add ( dependency . replace ( ' ' , '@' ) ) ; } ) ; let dependenciesMap = new Map ( ) ; for ( ; goModGraphIndex < goModGraph . length ; goModGraphIndex ++ ) { let dependency = dependenciesMap . get ( goModGraph [ goModGraphIndex ] . package ) || [ ] ; if ( ! goListPackages . has ( goModGraph [ goModGraphIndex ] . dependency ) ) { continue ; } dependency . push ( goModGraph [ goModGraphIndex ] . dependency ) ; dependenciesMap . set ( goModGraph [ goModGraphIndex ] . package , dependency ) ; } directDependenciesGeneralInfos . forEach ( generalInfo => { this . addChild ( new DependenciesTreeNode ( generalInfo , this . getTreeCollapsibleState ( dependenciesMap , generalInfo ) ) ) ; } ) ; return dependenciesMap ; } private populateDependenciesTree ( dependenciesMap , dependenciesTreeNode , quickScan ) { if ( this . hasLoop ( dependenciesTreeNode ) ) { return ; } this . addComponentToScan ( dependenciesTreeNode , quickScan ) ; let childDependencies = dependenciesMap . get ( dependenciesTreeNode . generalInfo . artifactId + '@v' + dependenciesTreeNode . generalInfo . version ) || [ ] ; childDependencies . forEach ( childDependency => { let nameVersionTuple = this . getNameVersionTuple ( childDependency ) ; let generalInfo = new GeneralInfo ( nameVersionTuple [ 0 ] , nameVersionTuple [ 1 ] , [ 'None' ] , '' , GoUtils . PKG_TYPE ) ; let grandchild = new DependenciesTreeNode ( generalInfo , this . getTreeCollapsibleState ( dependenciesMap , generalInfo ) , dependenciesTreeNode ) ; this . populateDependenciesTree ( dependenciesMap , grandchild , quickScan ) ; } ) ; } private hasLoop ( dependenciesTreeNode ) { let parent = dependenciesTreeNode . parent ; while ( parent ) { if ( parent . generalInfo ?. getComponentId ( ) === dependenciesTreeNode . generalInfo ?. getComponentId ( ) ) { this . _treesManager . logManager . logMessage ( 'Loop detected in ' + dependenciesTreeNode . generalInfo . artifactId , 'DEBUG' ) ; return true ; } parent = parent . parent ; } return false ; } private addComponentToScan ( dependenciesTreeNode , quickScan ) { let componentId = dependenciesTreeNode . generalInfo . artifactId + ':' + dependenciesTreeNode . generalInfo . version ; if ( ! quickScan || ! this . _treesManager . scanCacheManager . isValid ( componentId ) ) { this . _componentsToScan . add ( new ComponentDetails ( GoTreeNode . COMPONENT_PREFIX + componentId ) ) ; } } private getNameVersionTuple ( value ) { let split = value . split ( '@v' ) ; return [ split [ 0 ] , split [ 1 ] ] ; } private getTreeCollapsibleState ( dependenciesMap , generalInfo ) { return dependenciesMap . has ( generalInfo . artifactId + '@v' + generalInfo . version ) ? vscode . TreeItemCollapsibleState . Collapsed : vscode . TreeItemCollapsibleState . None ; } } class PackageDependencyPair { constructor ( private _package , private _dependency ) { } public get package ( ) { return this . _package ; } public get dependency ( ) { return this . _dependency ; } }