-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathrepo.go
185 lines (167 loc) · 4.95 KB
/
repo.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// File contains functions related to handling repository
// clone, push, pull, and remote-setting events.
package main
import (
"bufio"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/spf13/viper"
)
// Recursively scan a file and create a map of the contents.
// The file should contain one public git URL per line.
func parseRepoList(list string) (map[string]string, error) {
file, err := os.Open(list)
if err != nil {
return nil, err
}
defer file.Close()
var repos = make(map[string]string)
scanner := bufio.NewScanner(file)
// Go through the file line by line.
for scanner.Scan() {
repo := scanner.Text()
// For the key/value in the map, set the key
// to the base of the repo (usually the repo
// name) and the value to the URL.
repos[path.Base(repo)] = scanner.Text()
}
return repos, scanner.Err()
}
// Clone or pull repositories defined in the passed in map.
func cloneRepos(repos map[string]string) error {
// Build the local repository path base used for output.
pathBase := path.Clean(viper.Get("path").(string))
// Loop through the repository map.
for name, url := range repos {
fmt.Printf("Attempting to clone repository: %s\n", name)
// Create the full output path for this repo.
out := filepath.Join(pathBase, name)
// Clone the repo to the output path.
_, err := git.PlainClone(out, false, &git.CloneOptions{
URL: url,
Progress: os.Stdout,
RemoteName: "snyk",
})
// Check if the repo was already cloned. If it was, attempt
// to fetch and pull it instead of clone.
if err != nil && err != git.ErrRepositoryAlreadyExists {
return err
} else if err == git.ErrRepositoryAlreadyExists {
fmt.Printf("%s already cloned, attempting to pull from upstream\n", name)
// Open previously cloned, local repo.
r, err := git.PlainOpen(out)
if err != nil {
return err
}
// Get the working directory for the repository
w, err := r.Worktree()
if err != nil {
return err
}
// Perform a fetch on upstream "snyk" remote.
err = r.Fetch(&git.FetchOptions{
RemoteName: "snyk",
RefSpecs: []config.RefSpec{
"refs/*:refs/*",
},
Progress: os.Stdout,
})
// Pull the latest changes from the origin remote and merge into the current branch.
err = w.Pull(&git.PullOptions{RemoteName: "snyk"})
if err != nil && err != git.NoErrAlreadyUpToDate && err != git.ErrNonFastForwardUpdate {
return err
} else if err == git.NoErrAlreadyUpToDate {
fmt.Printf("%s already up to date, nothing to pull\n", name)
} else if err == git.ErrNonFastForwardUpdate {
fmt.Printf("%s has been modified locally, cannot merge from upstream", name)
}
}
}
return nil
}
// Control function to call each individual SCM creation
// function located in their respective files.
func createRemoteRepos(repos map[string]string) error {
// Loop through repo list and call function to create
// remote repository for each SCM.
for name := range repos {
err := createGitHubRepo(name)
if err != nil {
return err
}
err = createGitLabRepo(name)
if err != nil {
return err
}
err = createBitBucketRepo(name)
if err != nil {
return err
}
err = createAzureRepo(name)
if err != nil {
return err
}
}
return nil
}
// Create local git remote entries and push upstream.
func pushUpstream(name string, remote string, giturl string, u interface{}, p interface{}) error {
// Build the local repository path base used for input.
pathBase := path.Clean(viper.Get("path").(string))
in := path.Join(pathBase, name)
fmt.Printf("Pushing latest %s to remote \"%s\"\n", name, remote)
// Open local git repository.
r, err := git.PlainOpen(in)
if err != nil {
return err
}
// Create a named remote (github, gitlab, etc.)
_, err = r.CreateRemote(&config.RemoteConfig{
Name: remote,
URLs: []string{giturl},
})
// Check if the remote was already present. If it was
// remove it and recreate it to ensure integrity.
if err != nil && !strings.Contains(err.Error(), "remote already exists") {
return err
} else if err != nil && strings.Contains(err.Error(), "remote already exists") {
err = r.DeleteRemote(remote)
if err != nil {
return err
}
_, err = r.CreateRemote(&config.RemoteConfig{
Name: remote,
URLs: []string{giturl},
})
if err != nil {
return err
}
}
// Setup push options to be used in the actual push.
pushOptions := git.PushOptions{
RemoteName: remote,
Progress: os.Stdout,
Force: true,
}
// If credentials are necessary for the SCM push, add them
// to the pushOptions variable.
if u != nil && p != nil {
pushOptions.Auth = &http.BasicAuth{
Username: u.(string),
Password: p.(string),
}
}
// Push the code to the upstream SCM.
err = r.Push(&pushOptions)
if err != nil && err != git.NoErrAlreadyUpToDate {
return err
}
fmt.Printf("%s on remote \"%s\" up to date\n", name, remote)
return nil
}