cloudfoundry/stratos

View on GitHub
src/jetstream/plugins/cfapppush/vcs.go

Summary

Maintainability
A
0 mins
Test Coverage
package cfapppush

// Based on https://github.com/golang/go/blob/master/src/cmd/go/internal/get/vcs.go

import (
    "bytes"
    "os"
    "os/exec"
    "strconv"
    "strings"

    log "github.com/sirupsen/logrus"
)

var vcsGit = &vcsCmd{
    name:             "Git",
    cmd:              "git",
    createCmd:        []string{"clone -c http.sslVerify={sslVerify} -b {branch} {repo} {dir} "},
    resetToCommitCmd: []string{"reset --hard {commit}"},
    checkoutCmd:      []string{"checkout refs/remotes/origin/{branch}"},
    headCmd:          []string{"rev-parse HEAD"},
}

// Currently only git is supported
func GetVCS() *vcsCmd {
    return vcsGit
}

type vcsCmd struct {
    name string
    cmd  string // name of binary to invoke command

    createCmd        []string // commands to download a fresh copy of a repository
    checkoutCmd      []string // commands to checkout a branch
    headCmd          []string // get current head commit
    resetToCommitCmd []string // reset branch to commit
}

func (vcs *vcsCmd) Create(skipSSL bool, dir string, repo string, branch string) error {
    for _, cmd := range vcs.createCmd {
        if err := vcs.run(".", cmd, "sslVerify", strconv.FormatBool(!skipSSL), "dir", dir, "repo", repo, "branch", branch); err != nil {
            return err
        }
    }
    return nil
}

func (vcs *vcsCmd) ResetBranchToCommit(dir string, commit string) error {
    for _, cmd := range vcs.resetToCommitCmd {
        if err := vcs.run(dir, cmd, "commit", commit); err != nil {
            return err
        }
    }
    return nil
}

func (vcs *vcsCmd) Checkout(dir string, branchRef string) error {
    for _, cmd := range vcs.checkoutCmd {
        if err := vcs.run(dir, cmd, "branch", branchRef); err != nil {
            log.Warnf("checkout error was: %s", err)
            return err
        }
    }
    return nil
}

func (vcs *vcsCmd) Head(dir string) (string, error) {
    var emptySlice []string
    for _, cmd := range vcs.headCmd {
        hash, err := vcs.run1(dir, cmd, emptySlice, false)
        if err != nil {
            return "", err
        }
        return string(hash), nil
    }
    return "", nil
}

func (v *vcsCmd) run(dir string, cmd string, keyval ...string) error {
    _, err := v.run1(dir, cmd, keyval, true)
    return err
}

func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool) ([]byte, error) {

    m := make(map[string]string)
    for i := 0; i < len(keyval); i += 2 {
        m[keyval[i]] = keyval[i+1]
    }
    args := strings.Fields(cmdline)
    for i, arg := range args {
        args[i] = expand(m, arg)
    }

    _, err := exec.LookPath(v.cmd)
    if err != nil {
        log.Warnf("Missing command. Make sure %s is in your path", v.cmd)
        return nil, err
    }

    cmd := exec.Command(v.cmd, args...)
    cmd.Dir = dir
    cmd.Env = EnvForDir(cmd.Dir, os.Environ())

    var buf bytes.Buffer
    cmd.Stdout = &buf
    cmd.Stderr = &buf
    err = cmd.Run()
    out := buf.Bytes()
    if err != nil {
        return out, err
    }
    return out, nil
}

func expand(match map[string]string, s string) string {
    for k, v := range match {
        s = strings.Replace(s, "{"+k+"}", v, -1)
    }
    return s
}

func EnvForDir(dir string, base []string) []string {
    return MergeEnvLists([]string{"PWD=" + dir}, base)
}

func MergeEnvLists(in, out []string) []string {
    out = append([]string(nil), out...)
NextVar:
    for _, inkv := range in {
        k := strings.SplitAfterN(inkv, "=", 2)[0]
        for i, outkv := range out {
            if strings.HasPrefix(outkv, k) {
                out[i] = inkv
                continue NextVar
            }
        }
        out = append(out, inkv)
    }
    return out
}