go.undefinedlabs.com/scopeagent@v0.4.2/agent/git.go (about) 1 package agent 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io/ioutil" 8 "os" 9 "os/exec" 10 "path/filepath" 11 "regexp" 12 "strconv" 13 "strings" 14 15 "github.com/google/uuid" 16 "go.undefinedlabs.com/scopeagent/env" 17 "go.undefinedlabs.com/scopeagent/tags" 18 ) 19 20 type ( 21 GitData struct { 22 Repository string 23 Commit string 24 SourceRoot string 25 Branch string 26 } 27 GitDiff struct { 28 Type string `json:"type" msgpack:"type"` 29 Version string `json:"version" msgpack:"version"` 30 Uuid string `json:"uuid" msgpack:"uuid"` 31 Files []DiffFileItem `json:"files" msgpack:"files"` 32 } 33 DiffFileItem struct { 34 Path string `json:"path" msgpack:"path"` 35 Added int `json:"added" msgpack:"added"` 36 Removed int `json:"removed" msgpack:"removed"` 37 Status string `json:"status" msgpack:"status"` 38 PreviousPath *string `json:"previousPath" msgpack:"previousPath"` 39 } 40 ) 41 42 var ( 43 refRegex = regexp.MustCompile(`(?m)ref:[ ]*(.*)$`) 44 remoteRegex = regexp.MustCompile(`(?m)^\[remote[ ]*\"(.*)\"[ ]*\]$`) 45 branchRegex = regexp.MustCompile(`(?m)^\[branch[ ]*\"(.*)\"[ ]*\]$`) 46 urlRegex = regexp.MustCompile(`(?m)url[ ]*=[ ]*(.*)$`) 47 mergeRegex = regexp.MustCompile(`(?m)merge[ ]*=[ ]*(.*)$`) 48 ) 49 50 // Gets the current git data 51 func getGitData() *GitData { 52 gitFolder, err := getGitFolder() 53 if err != nil { 54 return nil 55 } 56 57 var repository, commit, sourceRoot, branch string 58 59 // Get source root 60 sourceRoot = filepath.Dir(gitFolder) 61 62 // Get commit hash 63 var mergePath string 64 if headFile, err := os.Open(filepath.Join(gitFolder, "HEAD")); err == nil { 65 defer headFile.Close() 66 if headBytes, err := ioutil.ReadAll(headFile); err == nil { 67 head := string(headBytes) 68 // HEAD data: https://git-scm.com/book/en/v2/Git-Internals-Git-References 69 refMatch := refRegex.FindStringSubmatch(head) 70 if len(refMatch) == 2 { 71 // Symbolic reference 72 mergePath = strings.TrimSpace(refMatch[1]) 73 if refFile, err := os.Open(filepath.Join(gitFolder, mergePath)); err == nil { 74 defer refFile.Close() 75 if refBytes, err := ioutil.ReadAll(refFile); err == nil { 76 commit = strings.TrimSpace(string(refBytes)) 77 } 78 } 79 } else { 80 // Detached head (Plain hash) 81 commit = strings.TrimSpace(head) 82 } 83 } 84 } 85 86 // Get repository and branch 87 if configFile, err := os.Open(filepath.Join(gitFolder, "config")); err == nil { 88 defer configFile.Close() 89 reader := bufio.NewReader(configFile) 90 scanner := bufio.NewScanner(reader) 91 92 var tmpBranch string 93 var intoRemoteBlock, intoBranchBlock bool 94 for scanner.Scan() { 95 line := scanner.Text() 96 97 if repository == "" { 98 if !intoRemoteBlock { 99 remoteMatch := remoteRegex.FindStringSubmatch(line) 100 if len(remoteMatch) == 2 { 101 intoRemoteBlock = remoteMatch[1] == "origin" 102 continue 103 } 104 } else { 105 urlMatch := urlRegex.FindStringSubmatch(line) 106 if len(urlMatch) == 2 { 107 repository = strings.TrimSpace(urlMatch[1]) 108 intoRemoteBlock = false 109 continue 110 } 111 } 112 } 113 114 if branch == "" { 115 if !intoBranchBlock { 116 branchMatch := branchRegex.FindStringSubmatch(line) 117 if len(branchMatch) == 2 { 118 tmpBranch = branchMatch[1] 119 intoBranchBlock = true 120 continue 121 } 122 } else { 123 mergeMatch := mergeRegex.FindStringSubmatch(line) 124 if len(mergeMatch) == 2 { 125 mergeData := strings.TrimSpace(mergeMatch[1]) 126 intoBranchBlock = false 127 if mergeData == mergePath { 128 branch = tmpBranch 129 continue 130 } 131 } 132 } 133 } 134 } 135 } 136 137 return &GitData{ 138 Repository: repository, 139 Commit: commit, 140 SourceRoot: sourceRoot, 141 Branch: branch, 142 } 143 } 144 145 func getGitFolder() (string, error) { 146 dir, err := os.Getwd() 147 if err != nil { 148 return "", nil 149 } 150 for { 151 rel, _ := filepath.Rel("/", dir) 152 // Exit the loop once we reach the basePath. 153 if rel == "." { 154 return "", errors.New("git folder not found") 155 } 156 gitPath := fmt.Sprintf("%v/.git", dir) 157 if pInfo, err := os.Stat(gitPath); err == nil && pInfo.IsDir() { 158 return gitPath, nil 159 } 160 // Going up! 161 dir += "/.." 162 } 163 } 164 165 func getGitDiff() *GitDiff { 166 var diff string 167 if diffBytes, err := exec.Command("git", "diff", "--numstat").Output(); err == nil { 168 diff = string(diffBytes) 169 } else { 170 return nil 171 } 172 173 reader := bufio.NewReader(strings.NewReader(diff)) 174 var files []DiffFileItem 175 for { 176 line, err := reader.ReadString('\n') 177 if err != nil { 178 break 179 } 180 diffItem := strings.Split(line, "\t") 181 added, _ := strconv.Atoi(diffItem[0]) 182 removed, _ := strconv.Atoi(diffItem[1]) 183 path := strings.TrimSuffix(diffItem[2], "\n") 184 185 files = append(files, DiffFileItem{ 186 Path: path, 187 Added: added, 188 Removed: removed, 189 Status: "Modified", 190 PreviousPath: nil, 191 }) 192 } 193 194 id, _ := uuid.NewRandom() 195 gitDiff := GitDiff{ 196 Type: "com.undefinedlabs.ugdsf", 197 Version: "0.1.0", 198 Uuid: id.String(), 199 Files: files, 200 } 201 return &gitDiff 202 } 203 204 func getGitInfoFromGitFolder() map[string]interface{} { 205 gitData := getGitData() 206 207 if gitData == nil { 208 return nil 209 } 210 211 gitInfo := map[string]interface{}{} 212 213 if gitData.Repository != "" { 214 gitInfo[tags.Repository] = gitData.Repository 215 } 216 if gitData.Commit != "" { 217 gitInfo[tags.Commit] = gitData.Commit 218 } 219 if gitData.SourceRoot != "" { 220 gitInfo[tags.SourceRoot] = gitData.SourceRoot 221 } 222 if gitData.Branch != "" { 223 gitInfo[tags.Branch] = gitData.Branch 224 } 225 226 return gitInfo 227 } 228 229 func getGitInfoFromEnv() map[string]interface{} { 230 gitInfo := map[string]interface{}{} 231 232 if repository, set := env.ScopeRepository.Tuple(); set && repository != "" { 233 gitInfo[tags.Repository] = repository 234 } 235 if commit, set := env.ScopeCommitSha.Tuple(); set && commit != "" { 236 gitInfo[tags.Commit] = commit 237 } 238 if sourceRoot, set := env.ScopeSourceRoot.Tuple(); set && sourceRoot != "" { 239 // We check if is a valid and existing folder 240 if fInfo, err := os.Stat(sourceRoot); err == nil && fInfo.IsDir() { 241 gitInfo[tags.SourceRoot] = sourceRoot 242 } 243 } 244 if branch, set := env.ScopeBranch.Tuple(); set && branch != "" { 245 gitInfo[tags.Branch] = branch 246 } 247 248 return gitInfo 249 }