-
Notifications
You must be signed in to change notification settings - Fork 1
/
scan.go
152 lines (132 loc) · 3.57 KB
/
scan.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/user"
"strings"
)
// getDotFilePath returns the dot file for the repos list.
// Creates it and the enclosing folder if it does not exist.
func getDotFilePath() string {
usr, err := user.Current()
if err != nil {
log.Fatal(err)
}
dotFile := usr.HomeDir + "/.gogitlocalstats"
return dotFile
}
// openFile opens the file located at `filePath`. Creates it if not existing.
func openFile(filePath string) *os.File {
f, err := os.OpenFile(filePath, os.O_APPEND|os.O_RDWR, 0755)
if err != nil {
if os.IsNotExist(err) {
// file does not exist
_, err = os.Create(filePath)
if err != nil {
panic(err)
}
} else {
// other error
panic(err)
}
}
return f
}
// parseFileLinesToSlice given a file path string, gets the content
// of each line and parses it to a slice of strings.
func parseFileLinesToSlice(filePath string) []string {
f := openFile(filePath)
defer f.Close()
var lines []string
scanner := bufio.NewScanner(f)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
if err != io.EOF {
panic(err)
}
}
return lines
}
// sliceContains returns true if `slice` contains `value`
func sliceContains(slice []string, value string) bool {
for _, v := range slice {
if v == value {
return true
}
}
return false
}
// joinSlices adds the element of the `new` slice
// into the `existing` slice, only if not already there
func joinSlices(new []string, existing []string) []string {
for _, i := range new {
if !sliceContains(existing, i) {
existing = append(existing, i)
}
}
return existing
}
// dumpStringsSliceToFile writes content to the file in path `filePath` (overwriting existing content)
func dumpStringsSliceToFile(repos []string, filePath string) {
content := strings.Join(repos, "\n")
ioutil.WriteFile(filePath, []byte(content), 0755)
}
// addNewSliceElementsToFile given a slice of strings representing paths, stores them
// to the filesystem
func addNewSliceElementsToFile(filePath string, newRepos []string) {
existingRepos := parseFileLinesToSlice(filePath)
repos := joinSlices(newRepos, existingRepos)
dumpStringsSliceToFile(repos, filePath)
}
// recursiveScanFolder starts the recursive search of git repositories
// living in the `folder` subtree
func recursiveScanFolder(folder string) []string {
return scanGitFolders(make([]string, 0), folder)
}
// scan scans a new folder for Git repositories
func scan(folder string) {
fmt.Printf("Found folders:\n\n")
repositories := recursiveScanFolder(folder)
filePath := getDotFilePath()
addNewSliceElementsToFile(filePath, repositories)
fmt.Printf("\n\nSuccessfully added\n\n")
}
// scanGitFolders returns a list of subfolders of `folder` ending with `.git`.
// Returns the base folder of the repo, the .git folder parent.
// Recursively searches in the subfolders by passing an existing `folders` slice.
func scanGitFolders(folders []string, folder string) []string {
// trim the last `/`
folder = strings.TrimSuffix(folder, "/")
f, err := os.Open(folder)
if err != nil {
log.Fatal(err)
}
files, err := f.Readdir(-1)
f.Close()
if err != nil {
log.Fatal(err)
}
var path string
for _, file := range files {
if file.IsDir() {
path = folder + "/" + file.Name()
if file.Name() == ".git" {
path = strings.TrimSuffix(path, "/.git")
fmt.Println(path)
folders = append(folders, path)
continue
}
if file.Name() == "vendor" || file.Name() == "node_modules" {
continue
}
folders = scanGitFolders(folders, path)
}
}
return folders
}