Skip to content

Commit

Permalink
feat(git): add more flags to the git command (#141)
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua-Anderson committed Jul 29, 2016
1 parent cc4f07e commit caf1fd0
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 23 deletions.
11 changes: 6 additions & 5 deletions cmd/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,18 @@ func AppCreate(id string, buildpack string, remote string, noRemote bool) error
if !noRemote {
if err = git.CreateRemote(s.Client.ControllerURL.Host, remote, app.ID); err != nil {
if err.Error() == "exit status 128" {
fmt.Println("To replace the existing git remote entry, run:")
fmt.Printf(" git remote rename deis deis.old && deis git:remote -a %s\n", app.ID)
msg := "A git remote with the name %s already exists. To overwrite this remote run:\n"
msg += "deis git:remote --force --remote %s --app %s"
return fmt.Errorf(msg, remote, remote, app.ID)
}
return err
}

fmt.Printf(remoteCreationMsg, remote, app.ID)
}

if noRemote {
fmt.Printf("If you want to add a git remote for this app later, use `deis git:remote -a %s`\n", app.ID)
} else {
fmt.Println("remote available at", git.RemoteURL(s.Client.ControllerURL.Host, app.ID))
}

return nil
Expand Down Expand Up @@ -264,7 +265,7 @@ func AppDestroy(appID, confirm string) error {
fmt.Printf("done in %ds\n", int(time.Since(startTime).Seconds()))

if gitSession {
return git.DeleteRemote(appID)
return GitRemove(appID)
}

return nil
Expand Down
55 changes: 53 additions & 2 deletions cmd/git.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,67 @@
package cmd

import (
"fmt"

"github.com/deis/workflow-cli/pkg/git"
)

const remoteCreationMsg = "Git remote %s successfully created for app %s.\n"
const remoteDeletionMsg = "Git remotes for app %s removed.\n"

// GitRemote creates a git remote for a deis app.
func GitRemote(appID, remote string) error {
func GitRemote(appID string, remote string, force bool) error {
s, appID, err := load(appID)

remoteURL, err := git.RemoteValue(remote)

if err != nil {
//If git remote doesn't exist, create it without issue
if err == git.ErrRemoteNotFound {
git.CreateRemote(s.Client.ControllerURL.Host, remote, appID)
fmt.Printf(remoteCreationMsg, remote, appID)
return nil
}

return err
}

expectedURL := git.RemoteURL(s.Client.ControllerURL.Host, appID)

if remoteURL == expectedURL {
fmt.Printf("Remote %s already exists and is correctly configured for app %s.\n", remote, appID)
return nil
}

if force {
fmt.Printf("Deleting git remote %s.\n", remote)
git.DeleteRemote(remote)
git.CreateRemote(s.Client.ControllerURL.Host, remote, appID)
fmt.Printf(remoteCreationMsg, remote, appID)
return nil
}

msg := "Remote %s already exists, please run 'deis git:remote -f' to overwrite\n"
msg += "Existing remote URL: %s\n"
msg += "When forced, will overwrite with: %s"

return fmt.Errorf(msg, remote, remoteURL, expectedURL)
}

// GitRemove removes a application git remote from a repository
func GitRemove(appID string) error {
s, appID, err := load(appID)

if err != nil {
return err
}

err = git.DeleteAppRemotes(s.Client.ControllerURL.Host, appID)

if err != nil {
return err
}

return git.CreateRemote(s.Client.ControllerURL.Host, remote, appID)
fmt.Printf(remoteDeletionMsg, appID)
return nil
}
27 changes: 26 additions & 1 deletion parser/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ func Git(argv []string) error {
Valid commands for git:
git:remote Adds git remote of application to repository
git:remove Removes git remote of application from repository
Use 'deis help [command]' to learn more.
`

switch argv[0] {
case "git:remote":
return gitRemote(argv)
case "git:remove":
return gitRemove(argv)
case "git":
fmt.Print(usage)
return nil
Expand All @@ -40,6 +43,8 @@ Options:
the uniquely identifiable name for the application.
-r --remote=REMOTE
name of remote to create. [default: deis]
-f --force
overwrite remote of the given name if it already exists.
`

args, err := docopt.Parse(usage, argv, true, "", false, true)
Expand All @@ -48,5 +53,25 @@ Options:
return err
}

return cmd.GitRemote(safeGetValue(args, "--app"), args["--remote"].(string))
return cmd.GitRemote(safeGetValue(args, "--app"), args["--remote"].(string), args["--force"].(bool))
}

func gitRemove(argv []string) error {
usage := `
Removes git remotes of application from repository.
Usage: deis git:remove [options]
Options:
-a --app=<app>
the uniquely identifiable name for the application.
`

args, err := docopt.Parse(usage, argv, true, "", false, true)

if err != nil {
return err
}

return cmd.GitRemove(safeGetValue(args, "--app"))
}
67 changes: 52 additions & 15 deletions pkg/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"
)

// ErrRemoteNotFound is returned when the remote cannot be found in git
var ErrRemoteNotFound = errors.New("Could not find remote matching app in 'git remote -v'")

// CreateRemote adds a git remote in the current directory.
func CreateRemote(host, remote, appID string) error {
cmd := exec.Command("git", "remote", "add", remote, RemoteURL(host, appID))
Expand All @@ -30,44 +34,62 @@ func CreateRemote(host, remote, appID string) error {
return err
}

fmt.Printf("Git remote %s added\n", remote)

return nil
}

// DeleteRemote removes a git remote in the current directory.
func DeleteRemote(appID string) error {
name, err := remoteNameFromAppID(appID)
// DeleteAppRemotes removes all git remotes corresponding to an app in the repository.
func DeleteAppRemotes(host, appID string) error {
names, err := remoteNamesFromAppID(host, appID)

if err != nil {
return err
}

if _, err = exec.Command("git", "remote", "remove", name).Output(); err != nil {
return err
for _, name := range names {
if err := DeleteRemote(name); err != nil {
return err
}
}

fmt.Printf("Git remote %s removed\n", name)

return nil
}

func remoteNameFromAppID(appID string) (string, error) {
// DeleteRemote removes a remote from the repository
func DeleteRemote(name string) error {
_, err := exec.Command("git", "remote", "remove", name).Output()
return err
}

// remoteNamesFromAppID returns the git remote names for an app
func remoteNamesFromAppID(host, appID string) ([]string, error) {
out, err := exec.Command("git", "remote", "-v").Output()

if err != nil {
return "", err
return []string{}, err
}

cmd := string(out)
remotes := []string{}

lines:
for _, line := range strings.Split(cmd, "\n") {
if strings.Contains(line, appID) {
return strings.Split(line, "\t")[0], nil
if strings.Contains(line, RemoteURL(host, appID)) {
name := strings.Split(line, "\t")[0]
// git remote -v can show duplicate remotes, so don't add a remote if it already has been added
for _, remote := range remotes {
if remote == name {
continue lines
}
}
remotes = append(remotes, name)
}
}

return "", errors.New("Could not find remote matching app in 'git remote -v'")
if len(remotes) == 0 {
return remotes, ErrRemoteNotFound
}

return remotes, nil
}

// DetectAppName detects if there is deis remote in git.
Expand Down Expand Up @@ -105,7 +127,7 @@ func findRemote(host string) (string, error) {
}
}

return "", errors.New("Could not find deis remote in 'git remote -v'")
return "", ErrRemoteNotFound
}

// RemoteURL returns the git URL of app.
Expand All @@ -121,3 +143,18 @@ func getBuilderHostname(host string) string {
hostTokens[0] = fmt.Sprintf("%s-builder", hostTokens[0])
return strings.Join(hostTokens, ".")
}

// RemoteValue gets the url that a git remote is set to.
func RemoteValue(name string) (string, error) {
out, err := exec.Command("git", "remote", "get-url", name).Output()

if err != nil {
// get the return code of the program and see if it equals not found
if err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitStatus() == 128 {
return "", ErrRemoteNotFound
}
return "", err
}

return strings.Trim(string(out), "\n"), nil
}

0 comments on commit caf1fd0

Please sign in to comment.