Integrate context for essential vcs cmds

get, update, and listVersions are changed. There's a lot more to do
here, but these are the most essential ones.
This commit is contained in:
sam boyer 2017-03-30 12:51:47 -04:00
Родитель 7e65302c73
Коммит 75dec7b2dc
7 изменённых файлов: 157 добавлений и 177 удалений

28
cmd.go
Просмотреть файл

@ -22,13 +22,12 @@ type monitoredCmd struct {
stderr *activityBuffer
}
func newMonitoredCmd(ctx context.Context, cmd *exec.Cmd, timeout time.Duration) *monitoredCmd {
func newMonitoredCmd(cmd *exec.Cmd, timeout time.Duration) *monitoredCmd {
stdout, stderr := newActivityBuffer(), newActivityBuffer()
cmd.Stdout, cmd.Stderr = stdout, stderr
return &monitoredCmd{
cmd: cmd,
timeout: timeout,
ctx: ctx,
stdout: stdout,
stderr: stderr,
}
@ -37,7 +36,12 @@ func newMonitoredCmd(ctx context.Context, cmd *exec.Cmd, timeout time.Duration)
// run will wait for the command to finish and return the error, if any. If the
// command does not show any activity for more than the specified timeout the
// process will be killed.
func (c *monitoredCmd) run() error {
func (c *monitoredCmd) run(ctx context.Context) error {
// Check for cancellation before even starting
if ctx.Err() != nil {
return ctx.Err()
}
ticker := time.NewTicker(c.timeout)
done := make(chan error, 1)
defer ticker.Stop()
@ -53,7 +57,7 @@ func (c *monitoredCmd) run() error {
return &timeoutError{c.timeout}
}
case <-c.ctx.Done():
case <-ctx.Done():
if err := c.cmd.Process.Kill(); err != nil {
return &killCmdError{err}
}
@ -70,8 +74,8 @@ func (c *monitoredCmd) hasTimedOut() bool {
c.stdout.lastActivity().Before(t)
}
func (c *monitoredCmd) combinedOutput() ([]byte, error) {
if err := c.run(); err != nil {
func (c *monitoredCmd) combinedOutput(ctx context.Context) ([]byte, error) {
if err := c.run(ctx); err != nil {
return c.stderr.buf.Bytes(), err
}
@ -121,12 +125,12 @@ func (e killCmdError) Error() string {
return fmt.Sprintf("error killing command: %s", e.err)
}
func runFromCwd(cmd string, args ...string) ([]byte, error) {
c := newMonitoredCmd(context.TODO(), exec.Command(cmd, args...), 2*time.Minute)
return c.combinedOutput()
func runFromCwd(ctx context.Context, cmd string, args ...string) ([]byte, error) {
c := newMonitoredCmd(exec.Command(cmd, args...), 2*time.Minute)
return c.combinedOutput(ctx)
}
func runFromRepoDir(repo vcs.Repo, cmd string, args ...string) ([]byte, error) {
c := newMonitoredCmd(context.TODO(), repo.CmdFromDir(cmd, args...), 2*time.Minute)
return c.combinedOutput()
func runFromRepoDir(ctx context.Context, repo vcs.Repo, cmd string, args ...string) ([]byte, error) {
c := newMonitoredCmd(repo.CmdFromDir(cmd, args...), 2*time.Minute)
return c.combinedOutput(ctx)
}

Просмотреть файл

@ -10,13 +10,18 @@ import (
)
func mkTestCmd(iterations int) *monitoredCmd {
return newMonitoredCmd(context.Background(),
return newMonitoredCmd(
exec.Command("./echosleep", "-n", fmt.Sprint(iterations)),
200*time.Millisecond,
)
}
func TestMonitoredCmd(t *testing.T) {
// Sleeps make this a bit slow
if testing.Short() {
t.Skip("skipping test with sleeps on short")
}
err := exec.Command("go", "build", "./_testdata/cmd/echosleep.go").Run()
if err != nil {
t.Errorf("Unable to build echosleep binary: %s", err)
@ -24,7 +29,7 @@ func TestMonitoredCmd(t *testing.T) {
defer os.Remove("./echosleep")
cmd := mkTestCmd(2)
err = cmd.run()
err = cmd.run(context.Background())
if err != nil {
t.Errorf("Expected command not to fail: %s", err)
}
@ -35,7 +40,7 @@ func TestMonitoredCmd(t *testing.T) {
}
cmd2 := mkTestCmd(10)
err = cmd2.run()
err = cmd2.run(context.Background())
if err == nil {
t.Error("Expected command to fail")
}

Просмотреть файл

@ -97,7 +97,7 @@ func (m maybeGitSource) try(ctx context.Context, cachedir string, c singleSource
}
// Pinging invokes the same action as calling listVersions, so just do that.
vl, err := src.listVersions()
vl, err := src.listVersions(ctx)
if err != nil {
return nil, 0, fmt.Errorf("remote repository at %s does not exist, or is inaccessible", ustr)
}
@ -154,7 +154,7 @@ func (m maybeGopkginSource) try(ctx context.Context, cachedir string, c singleSo
}
// Pinging invokes the same action as calling listVersions, so just do that.
vl, err := src.listVersions()
vl, err := src.listVersions(ctx)
if err != nil {
return nil, 0, fmt.Errorf("remote repository at %s does not exist, or is inaccessible", ustr)
}

Просмотреть файл

@ -463,7 +463,7 @@ func (sg *sourceGateway) require(ctx context.Context, wanted sourceState) (errSt
}
case sourceExistsLocally:
if !sg.src.existsLocally(ctx) {
err = sg.src.initLocal()
err = sg.src.initLocal(ctx)
if err == nil {
addlState |= sourceHasLatestLocally
} else {
@ -472,12 +472,12 @@ func (sg *sourceGateway) require(ctx context.Context, wanted sourceState) (errSt
}
case sourceHasLatestVersionList:
var pvl []PairedVersion
pvl, err = sg.src.listVersions()
pvl, err = sg.src.listVersions(ctx)
if err != nil {
sg.cache.storeVersionMap(pvl, true)
}
case sourceHasLatestLocally:
err = sg.src.updateLocal()
err = sg.src.updateLocal(ctx)
}
if err != nil {
@ -502,9 +502,9 @@ type source interface {
existsLocally(context.Context) bool
existsUpstream(context.Context) bool
upstreamURL() string
initLocal() error
updateLocal() error
listVersions() ([]PairedVersion, error)
initLocal(context.Context) error
updateLocal(context.Context) error
listVersions(context.Context) ([]PairedVersion, error)
getManifestAndLock(ProjectRoot, Revision, ProjectAnalyzer) (Manifest, Lock, error)
listPackages(ProjectRoot, Revision) (pkgtree.PackageTree, error)
revisionPresentIn(Revision) (bool, error)
@ -524,6 +524,7 @@ func (bs *baseVCSSource) existsLocally(ctx context.Context) bool {
return bs.crepo.r.CheckLocal()
}
// TODO reimpl for git
func (bs *baseVCSSource) existsUpstream(ctx context.Context) bool {
return bs.crepo.r.Ping()
}
@ -557,9 +558,9 @@ func (bs *baseVCSSource) revisionPresentIn(r Revision) (bool, error) {
// initLocal clones/checks out the upstream repository to disk for the first
// time.
func (bs *baseVCSSource) initLocal() error {
func (bs *baseVCSSource) initLocal(ctx context.Context) error {
bs.crepo.mut.Lock()
err := bs.crepo.r.Get()
err := bs.crepo.r.get(ctx)
bs.crepo.mut.Unlock()
if err != nil {
return unwrapVcsErr(err)
@ -569,9 +570,9 @@ func (bs *baseVCSSource) initLocal() error {
// updateLocal ensures the local data we have about the source is fully up to date
// with what's out there over the network.
func (bs *baseVCSSource) updateLocal() error {
func (bs *baseVCSSource) updateLocal(ctx context.Context) error {
bs.crepo.mut.Lock()
err := bs.crepo.r.Update()
err := bs.crepo.r.update(ctx)
bs.crepo.mut.Unlock()
if err != nil {
return unwrapVcsErr(err)

Просмотреть файл

@ -55,7 +55,8 @@ func testGitSourceInteractions(t *testing.T) {
url: u,
}
isrc, state, err := mb.try(context.Background(), cpath, newMemoryCache())
ctx := context.Background()
isrc, state, err := mb.try(ctx, cpath, newMemoryCache())
if err != nil {
t.Errorf("Unexpected error while setting up gitSource for test repo: %s", err)
rf()
@ -67,7 +68,7 @@ func testGitSourceInteractions(t *testing.T) {
t.Errorf("Expected return state to be %v, got %v", wantstate, state)
}
err = isrc.initLocal()
err = isrc.initLocal(ctx)
if err != nil {
t.Errorf("Error on cloning git repo: %s", err)
rf()
@ -85,7 +86,7 @@ func testGitSourceInteractions(t *testing.T) {
t.Errorf("Expected %s as source URL, got %s", un, src.upstreamURL())
}
pvlist, err := src.listVersions()
pvlist, err := src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from git repo: %s", err)
rf()
@ -161,7 +162,8 @@ func testGopkginSourceInteractions(t *testing.T) {
major: major,
}
isrc, state, err := mb.try(context.Background(), cpath, newMemoryCache())
ctx := context.Background()
isrc, state, err := mb.try(ctx, cpath, newMemoryCache())
if err != nil {
t.Errorf("Unexpected error while setting up gopkginSource for test repo: %s", err)
return
@ -172,7 +174,7 @@ func testGopkginSourceInteractions(t *testing.T) {
t.Errorf("Expected return state to be %v, got %v", wantstate, state)
}
err = isrc.initLocal()
err = isrc.initLocal(ctx)
if err != nil {
t.Errorf("Error on cloning git repo: %s", err)
rf()
@ -201,7 +203,7 @@ func testGopkginSourceInteractions(t *testing.T) {
t.Errorf("Revision %s that should exist was not present", rev)
}
pvlist, err := src.listVersions()
pvlist, err := src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from hg repo: %s", err)
}
@ -217,7 +219,7 @@ func testGopkginSourceInteractions(t *testing.T) {
}
// Run again, this time to ensure cache outputs correctly
pvlist, err = src.listVersions()
pvlist, err = src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from hg repo: %s", err)
}
@ -304,7 +306,8 @@ func testBzrSourceInteractions(t *testing.T) {
url: u,
}
isrc, state, err := mb.try(context.Background(), cpath, newMemoryCache())
ctx := context.Background()
isrc, state, err := mb.try(ctx, cpath, newMemoryCache())
if err != nil {
t.Errorf("Unexpected error while setting up bzrSource for test repo: %s", err)
rf()
@ -316,7 +319,7 @@ func testBzrSourceInteractions(t *testing.T) {
t.Errorf("Expected return state to be %v, got %v", wantstate, state)
}
err = isrc.initLocal()
err = isrc.initLocal(ctx)
if err != nil {
t.Errorf("Error on cloning git repo: %s", err)
rf()
@ -349,7 +352,7 @@ func testBzrSourceInteractions(t *testing.T) {
t.Errorf("Revision that should exist was not present")
}
pvlist, err := src.listVersions()
pvlist, err := src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from bzr repo: %s", err)
}
@ -365,7 +368,7 @@ func testBzrSourceInteractions(t *testing.T) {
}
// Run again, this time to ensure cache outputs correctly
pvlist, err = src.listVersions()
pvlist, err = src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from bzr repo: %s", err)
}
@ -420,7 +423,8 @@ func testHgSourceInteractions(t *testing.T) {
url: u,
}
isrc, state, err := mb.try(context.Background(), cpath, newMemoryCache())
ctx := context.Background()
isrc, state, err := mb.try(ctx, cpath, newMemoryCache())
if err != nil {
t.Errorf("Unexpected error while setting up hgSource for test repo: %s", err)
return
@ -431,7 +435,7 @@ func testHgSourceInteractions(t *testing.T) {
t.Errorf("Expected return state to be %v, got %v", wantstate, state)
}
err = isrc.initLocal()
err = isrc.initLocal(ctx)
if err != nil {
t.Errorf("Error on cloning git repo: %s", err)
rf()
@ -459,7 +463,7 @@ func testHgSourceInteractions(t *testing.T) {
t.Errorf("Revision that should exist was not present")
}
pvlist, err := src.listVersions()
pvlist, err := src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from hg repo: %s", err)
}
@ -475,7 +479,7 @@ func testHgSourceInteractions(t *testing.T) {
}
// Run again, this time to ensure cache outputs correctly
pvlist, err = src.listVersions()
pvlist, err = src.listVersions(ctx)
if err != nil {
t.Errorf("Unexpected error getting version pairs from hg repo: %s", err)
}

Просмотреть файл

@ -1,9 +1,8 @@
package gps
import (
"bytes"
"context"
"encoding/xml"
"io/ioutil"
"os"
"path/filepath"
"runtime"
@ -13,6 +12,14 @@ import (
"github.com/Masterminds/vcs"
)
type ctxRepo interface {
vcs.Repo
get(context.Context) error
update(context.Context) error
//updateVersion(context.Context) error
//ping(context.Context) (bool, error)
}
// original implementation of these methods come from
// https://github.com/Masterminds/vcs
@ -20,80 +27,86 @@ type gitRepo struct {
*vcs.GitRepo
}
func (r *gitRepo) Get() error {
out, err := runFromCwd("git", "clone", "--recursive", r.Remote(), r.LocalPath())
func newVcsRemoteErrorOr(msg string, err error, out string) error {
if err == context.Canceled || err == context.DeadlineExceeded {
return err
}
return vcs.NewRemoteError(msg, err, out)
}
// There are some windows cases where Git cannot create the parent directory,
// if it does not already exist, to the location it's trying to create the
// repo. Catch that error and try to handle it.
func newVcsLocalErrorOr(msg string, err error, out string) error {
if err == context.Canceled || err == context.DeadlineExceeded {
return err
}
return vcs.NewLocalError(msg, err, out)
}
func (r *gitRepo) get(ctx context.Context) error {
out, err := runFromCwd(ctx, "git", "clone", "--recursive", r.Remote(), r.LocalPath())
// There are some windows cases where Git cannot create the parent
// directory, of the location where it's trying to create the repo. Catch
// that error and try to handle it.
if err != nil && r.isUnableToCreateDir(err) {
basePath := filepath.Dir(filepath.FromSlash(r.LocalPath()))
if _, err := os.Stat(basePath); os.IsNotExist(err) {
err = os.MkdirAll(basePath, 0755)
if err != nil {
return vcs.NewLocalError("unable to create directory", err, "")
return newVcsLocalErrorOr("unable to create directory", err, "")
}
out, err = runFromCwd("git", "clone", r.Remote(), r.LocalPath())
out, err = runFromCwd(ctx, "git", "clone", r.Remote(), r.LocalPath())
if err != nil {
return vcs.NewRemoteError("unable to get repository", err, string(out))
return newVcsRemoteErrorOr("unable to get repository", err, string(out))
}
return err
}
} else if err != nil {
return vcs.NewRemoteError("unable to get repository", err, string(out))
return newVcsRemoteErrorOr("unable to get repository", err, string(out))
}
return nil
}
func (r *gitRepo) Update() error {
func (r *gitRepo) update(ctx context.Context) error {
// Perform a fetch to make sure everything is up to date.
//out, err := runFromRepoDir(r, "git", "fetch", "--tags", "--prune", r.RemoteLocation)
out, err := runFromRepoDir(r, "git", "fetch", "--tags", r.RemoteLocation)
//out, err := runFromRepoDir(ctx, r, "git", "fetch", "--tags", "--prune", r.RemoteLocation)
out, err := runFromRepoDir(ctx, r, "git", "fetch", "--tags", r.RemoteLocation)
if err != nil {
return vcs.NewRemoteError("unable to update repository", err, string(out))
return newVcsRemoteErrorOr("unable to update repository", err, string(out))
}
return nil
}
// When in a detached head state, such as when an individual commit is checked
// out do not attempt a pull. It will cause an error.
detached, err := r.isDetachedHead()
func (r *gitRepo) updateVersion(ctx context.Context, v string) error {
out, err := runFromRepoDir(ctx, r, "git", "checkout", v)
if err != nil {
return vcs.NewLocalError("unable to update repository", err, "")
return newVcsLocalErrorOr("Unable to update checked out version", err, string(out))
}
if detached {
return nil
}
out, err = runFromRepoDir(r, "git", "pull")
if err != nil {
return vcs.NewRemoteError("unable to update repository", err, string(out))
}
return r.defendAgainstSubmodules()
return r.defendAgainstSubmodules(ctx)
}
// defendAgainstSubmodules tries to keep repo state sane in the event of
// submodules. Or nested submodules. What a great idea, submodules.
func (r *gitRepo) defendAgainstSubmodules() error {
func (r *gitRepo) defendAgainstSubmodules(ctx context.Context) error {
// First, update them to whatever they should be, if there should happen to be any.
out, err := runFromRepoDir(r, "git", "submodule", "update", "--init", "--recursive")
out, err := runFromRepoDir(ctx, r, "git", "submodule", "update", "--init", "--recursive")
if err != nil {
return vcs.NewLocalError("unexpected error while defensively updating submodules", err, string(out))
return newVcsLocalErrorOr("unexpected error while defensively updating submodules", err, string(out))
}
// Now, do a special extra-aggressive clean in case changing versions caused
// one or more submodules to go away.
out, err = runFromRepoDir(r, "git", "clean", "-x", "-d", "-f", "-f")
out, err = runFromRepoDir(ctx, r, "git", "clean", "-x", "-d", "-f", "-f")
if err != nil {
return vcs.NewLocalError("unexpected error while defensively cleaning up after possible derelict submodule directories", err, string(out))
return newVcsLocalErrorOr("unexpected error while defensively cleaning up after possible derelict submodule directories", err, string(out))
}
// Then, repeat just in case there are any nested submodules that went away.
out, err = runFromRepoDir(r, "git", "submodule", "foreach", "--recursive", "git", "clean", "-x", "-d", "-f", "-f")
out, err = runFromRepoDir(ctx, r, "git", "submodule", "foreach", "--recursive", "git", "clean", "-x", "-d", "-f", "-f")
if err != nil {
return vcs.NewLocalError("unexpected error while defensively cleaning up after possible derelict nested submodule directories", err, string(out))
return newVcsLocalErrorOr("unexpected error while defensively cleaning up after possible derelict nested submodule directories", err, string(out))
}
return nil
@ -117,52 +130,36 @@ func (r *gitRepo) isUnableToCreateDir(err error) bool {
return false
}
// isDetachedHead will detect if git repo is in "detached head" state.
func (r *gitRepo) isDetachedHead() (bool, error) {
p := filepath.Join(r.LocalPath(), ".git", "HEAD")
contents, err := ioutil.ReadFile(p)
if err != nil {
return false, err
}
contents = bytes.TrimSpace(contents)
if bytes.HasPrefix(contents, []byte("ref: ")) {
return false, nil
}
return true, nil
}
type bzrRepo struct {
*vcs.BzrRepo
}
func (r *bzrRepo) Get() error {
func (r *bzrRepo) get(ctx context.Context) error {
basePath := filepath.Dir(filepath.FromSlash(r.LocalPath()))
if _, err := os.Stat(basePath); os.IsNotExist(err) {
err = os.MkdirAll(basePath, 0755)
if err != nil {
return vcs.NewLocalError("unable to create directory", err, "")
return newVcsLocalErrorOr("unable to create directory", err, "")
}
}
out, err := runFromCwd("bzr", "branch", r.Remote(), r.LocalPath())
out, err := runFromCwd(ctx, "bzr", "branch", r.Remote(), r.LocalPath())
if err != nil {
return vcs.NewRemoteError("unable to get repository", err, string(out))
return newVcsRemoteErrorOr("unable to get repository", err, string(out))
}
return nil
}
func (r *bzrRepo) Update() error {
out, err := runFromRepoDir(r, "bzr", "pull")
func (r *bzrRepo) update(ctx context.Context) error {
out, err := runFromRepoDir(ctx, r, "bzr", "pull")
if err != nil {
return vcs.NewRemoteError("unable to update repository", err, string(out))
return newVcsRemoteErrorOr("unable to update repository", err, string(out))
}
out, err = runFromRepoDir(r, "bzr", "update")
out, err = runFromRepoDir(ctx, r, "bzr", "update")
if err != nil {
return vcs.NewRemoteError("unable to update repository", err, string(out))
return newVcsRemoteErrorOr("unable to update repository", err, string(out))
}
return nil
@ -172,33 +169,33 @@ type hgRepo struct {
*vcs.HgRepo
}
func (r *hgRepo) Get() error {
out, err := runFromCwd("hg", "clone", r.Remote(), r.LocalPath())
func (r *hgRepo) get(ctx context.Context) error {
out, err := runFromCwd(ctx, "hg", "clone", r.Remote(), r.LocalPath())
if err != nil {
return vcs.NewRemoteError("unable to get repository", err, string(out))
return newVcsRemoteErrorOr("unable to get repository", err, string(out))
}
return nil
}
func (r *hgRepo) Update() error {
return r.UpdateVersion(``)
func (r *hgRepo) update(ctx context.Context) error {
return r.updateVersion(ctx, "")
}
func (r *hgRepo) UpdateVersion(version string) error {
out, err := runFromRepoDir(r, "hg", "pull")
func (r *hgRepo) updateVersion(ctx context.Context, version string) error {
out, err := runFromRepoDir(ctx, r, "hg", "pull")
if err != nil {
return vcs.NewRemoteError("unable to update checked out version", err, string(out))
return newVcsRemoteErrorOr("unable to update checked out version", err, string(out))
}
if len(strings.TrimSpace(version)) > 0 {
out, err = runFromRepoDir(r, "hg", "update", version)
out, err = runFromRepoDir(ctx, r, "hg", "update", version)
} else {
out, err = runFromRepoDir(r, "hg", "update")
out, err = runFromRepoDir(ctx, r, "hg", "update")
}
if err != nil {
return vcs.NewRemoteError("unable to update checked out version", err, string(out))
return newVcsRemoteErrorOr("unable to update checked out version", err, string(out))
}
return nil
@ -208,7 +205,7 @@ type svnRepo struct {
*vcs.SvnRepo
}
func (r *svnRepo) Get() error {
func (r *svnRepo) get(ctx context.Context) error {
remote := r.Remote()
if strings.HasPrefix(remote, "/") {
remote = "file://" + remote
@ -216,33 +213,34 @@ func (r *svnRepo) Get() error {
remote = "file:///" + remote
}
out, err := runFromCwd("svn", "checkout", remote, r.LocalPath())
out, err := runFromCwd(ctx, "svn", "checkout", remote, r.LocalPath())
if err != nil {
return vcs.NewRemoteError("unable to get repository", err, string(out))
return newVcsRemoteErrorOr("unable to get repository", err, string(out))
}
return nil
}
func (r *svnRepo) Update() error {
out, err := runFromRepoDir(r, "svn", "update")
func (r *svnRepo) update(ctx context.Context) error {
out, err := runFromRepoDir(ctx, r, "svn", "update")
if err != nil {
return vcs.NewRemoteError("unable to update repository", err, string(out))
return newVcsRemoteErrorOr("unable to update repository", err, string(out))
}
return err
}
func (r *svnRepo) UpdateVersion(version string) error {
out, err := runFromRepoDir(r, "svn", "update", "-r", version)
func (r *svnRepo) updateVersion(ctx context.Context, version string) error {
out, err := runFromRepoDir(ctx, r, "svn", "update", "-r", version)
if err != nil {
return vcs.NewRemoteError("unable to update checked out version", err, string(out))
return newVcsRemoteErrorOr("unable to update checked out version", err, string(out))
}
return nil
}
func (r *svnRepo) CommitInfo(id string) (*vcs.CommitInfo, error) {
ctx := context.TODO()
// There are cases where Svn log doesn't return anything for HEAD or BASE.
// svn info does provide details for these but does not have elements like
// the commit message.
@ -255,15 +253,15 @@ func (r *svnRepo) CommitInfo(id string) (*vcs.CommitInfo, error) {
Commit commit `xml:"entry>commit"`
}
out, err := runFromRepoDir(r, "svn", "info", "-r", id, "--xml")
out, err := runFromRepoDir(ctx, r, "svn", "info", "-r", id, "--xml")
if err != nil {
return nil, vcs.NewLocalError("unable to retrieve commit information", err, string(out))
return nil, newVcsLocalErrorOr("unable to retrieve commit information", err, string(out))
}
infos := new(info)
err = xml.Unmarshal(out, &infos)
if err != nil {
return nil, vcs.NewLocalError("unable to retrieve commit information", err, string(out))
return nil, newVcsLocalErrorOr("unable to retrieve commit information", err, string(out))
}
id = infos.Commit.Revision
@ -272,9 +270,9 @@ func (r *svnRepo) CommitInfo(id string) (*vcs.CommitInfo, error) {
}
}
out, err := runFromRepoDir(r, "svn", "log", "-r", id, "--xml")
out, err := runFromRepoDir(ctx, r, "svn", "log", "-r", id, "--xml")
if err != nil {
return nil, vcs.NewRemoteError("unable to retrieve commit information", err, string(out))
return nil, newVcsRemoteErrorOr("unable to retrieve commit information", err, string(out))
}
type logentry struct {
@ -291,7 +289,7 @@ func (r *svnRepo) CommitInfo(id string) (*vcs.CommitInfo, error) {
logs := new(log)
err = xml.Unmarshal(out, &logs)
if err != nil {
return nil, vcs.NewLocalError("unable to retrieve commit information", err, string(out))
return nil, newVcsLocalErrorOr("unable to retrieve commit information", err, string(out))
}
if len(logs.Logs) == 0 {
@ -307,7 +305,7 @@ func (r *svnRepo) CommitInfo(id string) (*vcs.CommitInfo, error) {
if len(logs.Logs[0].Date) > 0 {
ci.Date, err = time.Parse(time.RFC3339Nano, logs.Logs[0].Date)
if err != nil {
return nil, vcs.NewLocalError("unable to retrieve commit information", err, string(out))
return nil, newVcsLocalErrorOr("unable to retrieve commit information", err, string(out))
}
}

Просмотреть файл

@ -2,6 +2,7 @@ package gps
import (
"bytes"
"context"
"fmt"
"os"
"os/exec"
@ -10,7 +11,6 @@ import (
"sync"
"github.com/Masterminds/semver"
"github.com/Masterminds/vcs"
"github.com/sdboyer/gps/internal/fs"
)
@ -21,6 +21,7 @@ type gitSource struct {
}
func (s *gitSource) exportRevisionTo(rev Revision, to string) error {
ctx := context.TODO()
r := s.crepo.r
if err := os.MkdirAll(to, 0777); err != nil {
@ -40,7 +41,7 @@ func (s *gitSource) exportRevisionTo(rev Revision, to string) error {
// could have an err here...but it's hard to imagine how?
defer fs.RenameWithFallback(bak, idx)
out, err := runFromRepoDir(r, "git", "read-tree", rev.String())
out, err := runFromRepoDir(ctx, r, "git", "read-tree", rev.String())
if err != nil {
return fmt.Errorf("%s: %s", out, err)
}
@ -56,7 +57,7 @@ func (s *gitSource) exportRevisionTo(rev Revision, to string) error {
// though we have a bunch of housekeeping to do to set up, then tear
// down, the sparse checkout controls, as well as restore the original
// index and HEAD.
out, err = runFromRepoDir(r, "git", "checkout-index", "-a", "--prefix="+to)
out, err = runFromRepoDir(ctx, r, "git", "checkout-index", "-a", "--prefix="+to)
if err != nil {
return fmt.Errorf("%s: %s", out, err)
}
@ -64,7 +65,7 @@ func (s *gitSource) exportRevisionTo(rev Revision, to string) error {
return nil
}
func (s *gitSource) listVersions() (vlist []PairedVersion, err error) {
func (s *gitSource) listVersions(ctx context.Context) (vlist []PairedVersion, err error) {
r := s.crepo.r
var out []byte
c := exec.Command("git", "ls-remote", r.Remote())
@ -184,8 +185,8 @@ type gopkginSource struct {
major uint64
}
func (s *gopkginSource) listVersions() ([]PairedVersion, error) {
ovlist, err := s.gitSource.listVersions()
func (s *gopkginSource) listVersions(ctx context.Context) ([]PairedVersion, error) {
ovlist, err := s.gitSource.listVersions(ctx)
if err != nil {
return nil, err
}
@ -251,27 +252,11 @@ type bzrSource struct {
baseVCSSource
}
func (s *bzrSource) update() error {
r := s.crepo.r
out, err := runFromRepoDir(r, "bzr", "pull")
if err != nil {
return vcs.NewRemoteError("Unable to update repository", err, string(out))
}
out, err = runFromRepoDir(r, "bzr", "update")
if err != nil {
return vcs.NewRemoteError("Unable to update repository", err, string(out))
}
return nil
}
func (s *bzrSource) listVersions() ([]PairedVersion, error) {
func (s *bzrSource) listVersions(ctx context.Context) ([]PairedVersion, error) {
r := s.crepo.r
// Now, list all the tags
out, err := runFromRepoDir(r, "bzr", "tags", "--show-ids", "-v")
out, err := runFromRepoDir(ctx, r, "bzr", "tags", "--show-ids", "-v")
if err != nil {
return nil, fmt.Errorf("%s: %s", err, string(out))
}
@ -279,7 +264,7 @@ func (s *bzrSource) listVersions() ([]PairedVersion, error) {
all := bytes.Split(bytes.TrimSpace(out), []byte("\n"))
var branchrev []byte
branchrev, err = runFromRepoDir(r, "bzr", "version-info", "--custom", "--template={revision_id}", "--revision=branch:.")
branchrev, err = runFromRepoDir(ctx, r, "bzr", "version-info", "--custom", "--template={revision_id}", "--revision=branch:.")
br := string(branchrev)
if err != nil {
return nil, fmt.Errorf("%s: %s", err, br)
@ -309,29 +294,12 @@ type hgSource struct {
baseVCSSource
}
// TODO dead code?
func (s *hgSource) update() error {
r := s.crepo.r
out, err := runFromRepoDir(r, "hg", "pull")
if err != nil {
return vcs.NewLocalError("Unable to update checked out version", err, string(out))
}
out, err = runFromRepoDir(r, "hg", "update")
if err != nil {
return vcs.NewLocalError("Unable to update checked out version", err, string(out))
}
return nil
}
func (s *hgSource) listVersions() ([]PairedVersion, error) {
func (s *hgSource) listVersions(ctx context.Context) ([]PairedVersion, error) {
var vlist []PairedVersion
r := s.crepo.r
// Now, list all the tags
out, err := runFromRepoDir(r, "hg", "tags", "--debug", "--verbose")
out, err := runFromRepoDir(ctx, r, "hg", "tags", "--debug", "--verbose")
if err != nil {
return nil, fmt.Errorf("%s: %s", err, string(out))
}
@ -365,7 +333,7 @@ func (s *hgSource) listVersions() ([]PairedVersion, error) {
// bookmarks next, because the presence of the magic @ bookmark has to
// determine how we handle the branches
var magicAt bool
out, err = runFromRepoDir(r, "hg", "bookmarks", "--debug")
out, err = runFromRepoDir(ctx, r, "hg", "bookmarks", "--debug")
if err != nil {
// better nothing than partial and misleading
return nil, fmt.Errorf("%s: %s", err, string(out))
@ -398,7 +366,7 @@ func (s *hgSource) listVersions() ([]PairedVersion, error) {
}
}
out, err = runFromRepoDir(r, "hg", "branches", "-c", "--debug")
out, err = runFromRepoDir(ctx, r, "hg", "branches", "-c", "--debug")
if err != nil {
// better nothing than partial and misleading
return nil, fmt.Errorf("%s: %s", err, string(out))
@ -437,7 +405,7 @@ type repo struct {
mut sync.RWMutex
// Object for direct repo interaction
r vcs.Repo
r ctxRepo
// Whether or not the cache repo is in sync (think dvcs) with upstream
synced bool