github.com/cobalt77/jfrog-client-go@v0.14.5/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/cobalt77/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 } 28 ) 29 30 const MAX_ENTRIES = 10000 31 32 func NewVcsDetals() *VcsCache { 33 return &VcsCache{vcsRootDir: sync.Map{}, vcsDir: sync.Map{}, vcsDirSize: new(int32)} 34 } 35 36 func (this *VcsCache) incCacheSize(num int32) { 37 atomic.AddInt32(this.vcsDirSize, num) 38 } 39 40 func (this *VcsCache) getCacheSize() int32 { 41 return atomic.LoadInt32(this.vcsDirSize) 42 } 43 44 // 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). 45 // otherwise, search in the parent folder and try: 46 // 1. search for .git, and save the details for the current dir and all subpath 47 // 2. .git not found, go to parent dir and repeat 48 // 3. not found on the root directory, add all subpath to cache with nil as a value 49 func (this *VcsCache) GetVcsDetails(path string) (revision, refUrl string, err error) { 50 keys := strings.Split(path, string(os.PathSeparator)) 51 var subPath string 52 var subPaths []string 53 var vcsDetailsResult *vcsDetails 54 for i := len(keys); i > 0; i-- { 55 subPath = strings.Join(keys[:i], string(os.PathSeparator)) 56 // Try to get from cache 57 if vcsDetails, found := this.searchCache(subPath); found { 58 if vcsDetails != nil { 59 revision, refUrl, vcsDetailsResult = vcsDetails.revision, vcsDetails.url, vcsDetails 60 } 61 break 62 } 63 // Begin directory search 64 revision, refUrl, err = tryGetGitDetails(subPath, this) 65 if revision != "" || refUrl != "" { 66 vcsDetailsResult = &vcsDetails{revision: revision, url: refUrl} 67 this.vcsRootDir.Store(subPath, vcsDetailsResult) 68 break 69 } 70 if err != nil { 71 return 72 } 73 subPaths = append(subPaths, subPath) 74 } 75 if size := len(subPaths); size > 0 { 76 this.clearCacheIfExceedsMax() 77 for _, v := range subPaths { 78 this.vcsDir.Store(v, vcsDetailsResult) 79 } 80 this.incCacheSize(int32(size)) 81 } 82 return 83 } 84 85 func (this *VcsCache) clearCacheIfExceedsMax() { 86 if this.getCacheSize() > MAX_ENTRIES { 87 this.vcsDir = sync.Map{} 88 this.vcsDirSize = new(int32) 89 } 90 } 91 92 func tryGetGitDetails(path string, this *VcsCache) (string, string, error) { 93 exists, err := fileutils.IsDirExists(filepath.Join(path, ".git"), false) 94 if exists { 95 return extractGitDetails(path) 96 } 97 return "", "", err 98 } 99 100 func extractGitDetails(path string) (string, string, error) { 101 gitService := NewGitManager(path) 102 if err := gitService.ReadConfig(); err != nil { 103 return "", "", err 104 } 105 return gitService.GetRevision(), gitService.GetUrl(), nil 106 } 107 108 func (this *VcsCache) searchCache(path string) (*vcsDetails, bool) { 109 if data, found := this.vcsDir.Load(path); found { 110 if vcsDetails, ok := data.(*vcsDetails); ok { 111 return vcsDetails, ok 112 } 113 } 114 if data, found := this.vcsRootDir.Load(path); found { 115 if vcsDetails, ok := data.(*vcsDetails); ok { 116 return vcsDetails, ok 117 } 118 } 119 return nil, false 120 }