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  }