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