github.com/mondo192/jfrog-client-go@v1.0.0/utils/vcsdetails.go (about) 1 package utils 2 3 import ( 4 "os" 5 "path/filepath" 6 "strings" 7 "sync" 8 "sync/atomic" 9 10 "github.com/mondo192/jfrog-client-go/utils/io/fileutils" 11 ) 12 13 type ( 14 VcsCache struct { 15 // Key - Path to the .git directory. 16 // Value - Reference to a struct, storing the URL and revision. 17 vcsRootDir sync.Map 18 // Key - Path to a directory. 19 // Value - Reference to a struct, storing the URL and revision from the upstream .git. Can also include nil, if there's no upstream .git. 20 vcsDir sync.Map 21 // The current size of vcsDir 22 vcsDirSize *int32 // Size of vcs folders entries 23 } 24 vcsDetails struct { 25 url string 26 revision string 27 branch string 28 } 29 ) 30 31 const MaxEntries = 10000 32 33 func NewVcsDetails() *VcsCache { 34 return &VcsCache{vcsRootDir: sync.Map{}, vcsDir: sync.Map{}, vcsDirSize: new(int32)} 35 } 36 37 func (vc *VcsCache) incCacheSize(num int32) { 38 atomic.AddInt32(vc.vcsDirSize, num) 39 } 40 41 func (vc *VcsCache) getCacheSize() int32 { 42 return atomic.LoadInt32(vc.vcsDirSize) 43 } 44 45 // Search for '.git' directory inside 'path', incase there is one, extract the details and add a new entry to the cache(key:path in the file system ,value: git revision & url). 46 // otherwise, search in the parent folder and try: 47 // 1. search for .git, and save the details for the current dir and all subpath 48 // 2. .git not found, go to parent dir and repeat 49 // 3. not found on the root directory, add all subpath to cache with nil as a value 50 func (vc *VcsCache) GetVcsDetails(path string) (revision, refUrl, branch string, err error) { 51 keys := strings.Split(path, string(os.PathSeparator)) 52 var subPath string 53 var subPaths []string 54 var vcsDetailsResult *vcsDetails 55 for i := len(keys); i > 0; i-- { 56 subPath = strings.Join(keys[:i], string(os.PathSeparator)) 57 // Try to get from cache 58 if vcsDetails, found := vc.searchCache(subPath); found { 59 if vcsDetails != nil { 60 revision, refUrl, branch, vcsDetailsResult = vcsDetails.revision, vcsDetails.url, vcsDetails.branch, vcsDetails 61 } 62 break 63 } 64 // Begin directory search 65 revision, refUrl, branch, err = tryGetGitDetails(subPath) 66 if revision != "" || refUrl != "" || branch != "" { 67 vcsDetailsResult = &vcsDetails{revision: revision, url: refUrl, branch: branch} 68 vc.vcsRootDir.Store(subPath, vcsDetailsResult) 69 break 70 } 71 if err != nil { 72 return 73 } 74 subPaths = append(subPaths, subPath) 75 } 76 if size := len(subPaths); size > 0 { 77 vc.clearCacheIfExceedsMax() 78 for _, v := range subPaths { 79 vc.vcsDir.Store(v, vcsDetailsResult) 80 } 81 vc.incCacheSize(int32(size)) 82 } 83 return 84 } 85 86 func (vc *VcsCache) clearCacheIfExceedsMax() { 87 if vc.getCacheSize() > MaxEntries { 88 vc.vcsDir = sync.Map{} 89 vc.vcsDirSize = new(int32) 90 } 91 } 92 93 func tryGetGitDetails(path string) (string, string, string, error) { 94 exists := fileutils.IsPathExists(filepath.Join(path, ".git"), false) 95 if exists { 96 return extractGitDetails(path) 97 } 98 return "", "", "", nil 99 } 100 101 func extractGitDetails(path string) (string, string, string, error) { 102 gitService := NewGitManager(path) 103 if err := gitService.ReadConfig(); err != nil { 104 return "", "", "", err 105 } 106 return gitService.GetRevision(), gitService.GetUrl(), gitService.GetBranch(), nil 107 } 108 109 func (vc *VcsCache) searchCache(path string) (*vcsDetails, bool) { 110 if data, found := vc.vcsDir.Load(path); found { 111 if vcsDetails, ok := data.(*vcsDetails); ok { 112 return vcsDetails, ok 113 } 114 } 115 if data, found := vc.vcsRootDir.Load(path); found { 116 if vcsDetails, ok := data.(*vcsDetails); ok { 117 return vcsDetails, ok 118 } 119 } 120 return nil, false 121 }