@@ -7,14 +7,36 @@ import (
77 "os/exec"
88 "path/filepath"
99 "strings"
10- "syscall"
1110)
1211
13- // ErrRemoteNotFound is returned when the remote cannot be found in git
14- var ErrRemoteNotFound = errors .New ("Could not find remote matching app in 'git remote -v'" )
12+ var (
13+ // ErrRemoteNotFound is returned when the remote cannot be found in git
14+ ErrRemoteNotFound = errors .New ("Could not find remote matching app in 'git remote -v'" )
15+ // ErrInvalidRepositoryList is an error returned if git returns unparsible output
16+ ErrInvalidRepositoryList = errors .New ("Invalid output in 'git remote -v'" )
17+ )
18+
19+ // Cmd is a method the exeutes the given git command and returns the output or the error.
20+ type Cmd func (cmd []string ) (string , error )
21+
22+ // remote defines a git remote's name and its url.
23+ type remote struct {
24+ Name string
25+ URL string
26+ }
27+
28+ // DefaultCmd is an implementation of Cmd that calls git.
29+ func DefaultCmd (cmd []string ) (string , error ) {
30+ out , err := exec .Command ("git" , cmd ... ).Output ()
31+ if err != nil {
32+ return string (out ), gitError (err .(* exec.ExitError ), cmd )
33+ }
34+
35+ return string (out ), nil
36+ }
1537
1638func gitError (err * exec.ExitError , cmd []string ) error {
17- msg := fmt .Sprintf ("Error when running '%s'\n " , strings .Join (cmd , " " ))
39+ msg := fmt .Sprintf ("Error when running 'git %s'\n " , strings .Join (cmd , " " ))
1840 out := string (err .Stderr )
1941 if out != "" {
2042 msg += strings .TrimSpace (out )
@@ -24,35 +46,27 @@ func gitError(err *exec.ExitError, cmd []string) error {
2446}
2547
2648// CreateRemote adds a git remote in the current directory.
27- func CreateRemote (host , remote , appID string ) error {
28- cmd := []string {"git" , "remote" , "add" , remote , RemoteURL (host , appID )}
29- if _ , err := exec .Command (cmd [0 ], cmd [1 :]... ).Output (); err != nil {
30- return gitError (err .(* exec.ExitError ), cmd )
31- }
32-
33- return nil
49+ func CreateRemote (cmd Cmd , host , name , appID string ) error {
50+ _ , err := cmd ([]string {"remote" , "add" , name , RepositoryURL (host , appID )})
51+ return err
3452}
3553
3654// Init creates a new git repository in the local directory.
37- func Init () error {
38- cmd := []string {"git" , "init" }
39- if _ , err := exec .Command (cmd [0 ], cmd [1 :]... ).Output (); err != nil {
40- return gitError (err .(* exec.ExitError ), cmd )
41- }
42-
43- return nil
55+ func Init (cmd Cmd ) error {
56+ _ , err := cmd ([]string {"init" })
57+ return err
4458}
4559
4660// DeleteAppRemotes removes all git remotes corresponding to an app in the repository.
47- func DeleteAppRemotes (host , appID string ) error {
48- names , err := remoteNamesFromAppID (host , appID )
61+ func DeleteAppRemotes (cmd Cmd , host , appID string ) error {
62+ names , err := remoteNamesFromAppID (cmd , host , appID )
4963
5064 if err != nil {
5165 return err
5266 }
5367
5468 for _ , name := range names {
55- if err := DeleteRemote (name ); err != nil {
69+ if err := DeleteRemote (cmd , name ); err != nil {
5670 return err
5771 }
5872 }
@@ -61,50 +75,36 @@ func DeleteAppRemotes(host, appID string) error {
6175}
6276
6377// DeleteRemote removes a remote from the repository
64- func DeleteRemote (name string ) error {
65- cmd := []string {"git" , "remote" , "remove" , name }
66- if _ , err := exec .Command (cmd [0 ], cmd [1 :]... ).Output (); err != nil {
67- return gitError (err .(* exec.ExitError ), cmd )
68- }
69-
70- return nil
78+ func DeleteRemote (cmd Cmd , name string ) error {
79+ _ , err := cmd ([]string {"remote" , "remove" , name })
80+ return err
7181}
7282
7383// remoteNamesFromAppID returns the git remote names for an app
74- func remoteNamesFromAppID (host , appID string ) ([]string , error ) {
75- cmd := []string {"git" , "remote" , "-v" }
76- out , err := exec .Command (cmd [0 ], cmd [1 :]... ).Output ()
77-
84+ func remoteNamesFromAppID (cmd Cmd , host , appID string ) ([]string , error ) {
85+ remotes , err := getRemotes (cmd )
7886 if err != nil {
79- return [] string {}, gitError ( err .( * exec. ExitError ), cmd )
87+ return nil , err
8088 }
8189
82- remotes := []string {}
83-
84- lines:
85- for _ , line := range strings .Split (string (out ), "\n " ) {
86- if strings .Contains (line , RemoteURL (host , appID )) {
87- name := strings .Split (line , "\t " )[0 ]
88- // git remote -v can show duplicate remotes, so don't add a remote if it already has been added
89- for _ , remote := range remotes {
90- if remote == name {
91- continue lines
92- }
93- }
94- remotes = append (remotes , name )
90+ var matchedRemotes []string
91+
92+ for _ , r := range remotes {
93+ if r .URL == RepositoryURL (host , appID ) {
94+ matchedRemotes = append (matchedRemotes , r .Name )
9595 }
9696 }
9797
98- if len (remotes ) == 0 {
99- return remotes , ErrRemoteNotFound
98+ if len (matchedRemotes ) == 0 {
99+ return nil , ErrRemoteNotFound
100100 }
101101
102- return remotes , nil
102+ return matchedRemotes , nil
103103}
104104
105105// DetectAppName detects if there is deis remote in git.
106- func DetectAppName (host string ) (string , error ) {
107- remote , err := findRemote (host )
106+ func DetectAppName (cmd Cmd , host string ) (string , error ) {
107+ remote , err := findRemote (cmd , host )
108108
109109 // Don't return an error if remote can't be found, return directory name instead.
110110 if err != nil {
@@ -116,30 +116,28 @@ func DetectAppName(host string) (string, error) {
116116 return strings .Split (ss [len (ss )- 1 ], "." )[0 ], nil
117117}
118118
119- func findRemote ( host string ) ( string , error ) {
120- cmd := [] string { "git" , "remote" , "-v" }
121- out , err := exec . Command (cmd [ 0 ], cmd [ 1 :] ... ). Output ( )
119+ // findRemote finds a remote name the uses a workflow git repository.
120+ func findRemote ( cmd Cmd , host string ) ( string , error ) {
121+ remotes , err := getRemotes (cmd )
122122 if err != nil {
123- return "" , gitError ( err .( * exec. ExitError ), cmd )
123+ return "" , err
124124 }
125125
126- // Strip off any trailing :port number after the host name.
127- host = strings .Split (host , ":" )[0 ]
128- builderHost := getBuilderHostname (host )
126+ // strip port from controller url and use it to find builder hostname
127+ builderHost := getBuilderHostname (strings .Split (host , ":" )[0 ])
129128
130- for _ , line := range strings .Split (string (out ), "\n " ) {
131- for _ , remote := range strings .Split (line , " " ) {
132- if strings .Contains (remote , host ) || strings .Contains (remote , builderHost ) {
133- return strings .Split (remote , "\t " )[1 ], nil
134- }
129+ // search for builder hostname in remote url
130+ for _ , r := range remotes {
131+ if strings .Contains (r .URL , builderHost ) {
132+ return r .URL , nil
135133 }
136134 }
137135
138136 return "" , ErrRemoteNotFound
139137}
140138
141- // RemoteURL returns the git URL of app.
142- func RemoteURL (host , appID string ) string {
139+ // RepositoryURL returns the git repository of an app.
140+ func RepositoryURL (host , appID string ) string {
143141 // Strip off any trailing :port number after the host name.
144142 host = strings .Split (host , ":" )[0 ]
145143 return fmt .Sprintf ("ssh://git@%s:2222/%s.git" , getBuilderHostname (host ), appID )
@@ -152,18 +150,43 @@ func getBuilderHostname(host string) string {
152150 return strings .Join (hostTokens , "." )
153151}
154152
155- // RemoteValue gets the url that a git remote is set to.
156- func RemoteValue (name string ) (string , error ) {
157- cmd := []string {"git" , "remote" , "get-url" , name }
158- out , err := exec .Command (cmd [0 ], cmd [1 :]... ).Output ()
153+ // RemoteURL retrives the url that a git remote is set to.
154+ func RemoteURL (cmd Cmd , name string ) (string , error ) {
155+ remotes , err := getRemotes (cmd )
156+ if err != nil {
157+ return "" , err
158+ }
159+
160+ for _ , r := range remotes {
161+ if r .Name == name {
162+ return r .URL , nil
163+ }
164+ }
159165
166+ return "" , ErrRemoteNotFound
167+ }
168+
169+ // getRemotes retrives all the git remotes from a repository
170+ func getRemotes (cmd Cmd ) ([]remote , error ) {
171+ out , err := cmd ([]string {"remote" , "-v" })
160172 if err != nil {
161- // get the return code of the program and see if it equals not found
162- if err .(* exec.ExitError ).Sys ().(syscall.WaitStatus ).ExitStatus () == 128 {
163- return "" , ErrRemoteNotFound
173+ return nil , err
174+ }
175+
176+ var remotes []remote
177+
178+ for _ , line := range strings .Split (out , "\n " ) {
179+ // git remote -v contains both push and fetch remotes.
180+ // They're generally identical, and deis only cares about push.
181+ if strings .HasSuffix (line , "(push)" ) {
182+ parts := strings .Split (line , "\t " )
183+ if len (parts ) < 2 {
184+ return remotes , ErrInvalidRepositoryList
185+ }
186+
187+ remotes = append (remotes , remote {Name : parts [0 ], URL : strings .Split (parts [1 ], " " )[0 ]})
164188 }
165- return "" , gitError (err .(* exec.ExitError ), cmd )
166189 }
167190
168- return strings . Trim ( string ( out ), " \n " ) , nil
191+ return remotes , nil
169192}
0 commit comments