Skip to content

Commit 6431902

Browse files
committed
Merge pull request #194 from vim-volt/config.get.fallback_git_cmd
[volt get] When clone/fetch/pull is failed, should try to run git command
2 parents 6dd54c0 + 53b706f commit 6431902

File tree

3 files changed

+136
-33
lines changed

3 files changed

+136
-33
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ strategy = "symlink"
142142
# * true (default): "volt get" creates skeleton plugconf file at "$VOLTPATH/plugconf/<repos>.vim"
143143
# * false: It does not creates skeleton plugconf file
144144
create_skeleton_plugconf = true
145+
146+
# * true (default): When "volt get" or "volt get -u" fail and "git" command is
147+
# installed, it tries to execute "git clone" or "git pull" as a fallback
148+
# * false: "volt get" or "volt get -u" won't try to execute fallback commands
149+
fallback_git_cmd = true
145150
```
146151

147152
## Self upgrade

cmd/get.go

Lines changed: 126 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import (
66
"fmt"
77
"io/ioutil"
88
"os"
9+
"os/exec"
910
"path/filepath"
11+
"runtime"
1012
"sort"
1113
"strings"
1214

15+
"gopkg.in/src-d/go-git.v4"
16+
1317
"github.com/vim-volt/volt/config"
1418
"github.com/vim-volt/volt/fileutil"
1519
"github.com/vim-volt/volt/gitutil"
@@ -20,7 +24,6 @@ import (
2024
"github.com/vim-volt/volt/transaction"
2125

2226
multierror "github.com/hashicorp/go-multierror"
23-
"gopkg.in/src-d/go-git.v4"
2427
)
2528

2629
func init() {
@@ -211,7 +214,7 @@ func (cmd *getCmd) doGet(reposPathList []pathutil.ReposPath, lockJSON *lockjson.
211214
repos = nil
212215
}
213216
if repos == nil || repos.Type == lockjson.ReposGitType {
214-
go cmd.getParallel(reposPath, repos, *cfg.Get.CreateSkeletonPlugconf, done)
217+
go cmd.getParallel(reposPath, repos, cfg, done)
215218
getCount++
216219
}
217220
}
@@ -310,11 +313,11 @@ const (
310313
// This function is executed in goroutine of each plugin.
311314
// 1. install plugin if it does not exist
312315
// 2. install plugconf if it does not exist and createPlugconf=true
313-
func (cmd *getCmd) getParallel(reposPath pathutil.ReposPath, repos *lockjson.Repos, createPlugconf bool, done chan<- getParallelResult) {
316+
func (cmd *getCmd) getParallel(reposPath pathutil.ReposPath, repos *lockjson.Repos, cfg *config.Config, done chan<- getParallelResult) {
314317
pluginDone := make(chan getParallelResult)
315-
go cmd.installPlugin(reposPath, repos, pluginDone)
318+
go cmd.installPlugin(reposPath, repos, cfg, pluginDone)
316319
pluginResult := <-pluginDone
317-
if pluginResult.err != nil || !createPlugconf {
320+
if pluginResult.err != nil || !*cfg.Get.CreateSkeletonPlugconf {
318321
done <- pluginResult
319322
return
320323
}
@@ -323,7 +326,7 @@ func (cmd *getCmd) getParallel(reposPath pathutil.ReposPath, repos *lockjson.Rep
323326
done <- (<-plugconfDone)
324327
}
325328

326-
func (cmd *getCmd) installPlugin(reposPath pathutil.ReposPath, repos *lockjson.Repos, done chan<- getParallelResult) {
329+
func (cmd *getCmd) installPlugin(reposPath pathutil.ReposPath, repos *lockjson.Repos, cfg *config.Config, done chan<- getParallelResult) {
327330
// true:upgrade, false:install
328331
fullReposPath := pathutil.FullReposPath(reposPath)
329332
doUpgrade := cmd.upgrade && pathutil.Exists(fullReposPath)
@@ -366,7 +369,7 @@ func (cmd *getCmd) installPlugin(reposPath pathutil.ReposPath, repos *lockjson.R
366369
}
367370
// Upgrade plugin
368371
logger.Debug("Upgrading " + reposPath + " ...")
369-
err := cmd.upgradePlugin(reposPath)
372+
err := cmd.upgradePlugin(reposPath, cfg)
370373
if err != git.NoErrAlreadyUpToDate && err != nil {
371374
result := errors.New("failed to upgrade plugin: " + err.Error())
372375
logger.Debug("Rollbacking " + fullReposPath + " ...")
@@ -389,7 +392,7 @@ func (cmd *getCmd) installPlugin(reposPath pathutil.ReposPath, repos *lockjson.R
389392
} else if doInstall {
390393
// Install plugin
391394
logger.Debug("Installing " + reposPath + " ...")
392-
err := cmd.clonePlugin(reposPath)
395+
err := cmd.clonePlugin(reposPath, cfg)
393396
if err != nil {
394397
result := errors.New("failed to install plugin: " + err.Error())
395398
logger.Debug("Rollbacking " + fullReposPath + " ...")
@@ -495,15 +498,15 @@ func (*getCmd) rollbackRepos(fullReposPath string) error {
495498
return nil
496499
}
497500

498-
func (cmd *getCmd) upgradePlugin(reposPath pathutil.ReposPath) error {
501+
func (cmd *getCmd) upgradePlugin(reposPath pathutil.ReposPath, cfg *config.Config) error {
499502
fullpath := pathutil.FullReposPath(reposPath)
500503

501504
repos, err := git.PlainOpen(fullpath)
502505
if err != nil {
503506
return err
504507
}
505508

506-
cfg, err := repos.Config()
509+
reposCfg, err := repos.Config()
507510
if err != nil {
508511
return err
509512
}
@@ -513,25 +516,16 @@ func (cmd *getCmd) upgradePlugin(reposPath pathutil.ReposPath) error {
513516
return err
514517
}
515518

516-
if cfg.Core.IsBare {
517-
return repos.Fetch(&git.FetchOptions{
518-
RemoteName: remote,
519-
})
519+
if reposCfg.Core.IsBare {
520+
return cmd.gitFetch(repos, fullpath, remote, cfg)
520521
} else {
521-
wt, err := repos.Worktree()
522-
if err != nil {
523-
return err
524-
}
525-
return wt.Pull(&git.PullOptions{
526-
RemoteName: remote,
527-
RecurseSubmodules: 10,
528-
})
522+
return cmd.gitPull(repos, fullpath, remote, cfg)
529523
}
530524
}
531525

532526
var errRepoExists = errors.New("repository exists")
533527

534-
func (cmd *getCmd) clonePlugin(reposPath pathutil.ReposPath) error {
528+
func (cmd *getCmd) clonePlugin(reposPath pathutil.ReposPath, cfg *config.Config) error {
535529
fullpath := pathutil.FullReposPath(reposPath)
536530
if pathutil.Exists(fullpath) {
537531
return errRepoExists
@@ -543,16 +537,7 @@ func (cmd *getCmd) clonePlugin(reposPath pathutil.ReposPath) error {
543537
}
544538

545539
// Clone repository to $VOLTPATH/repos/{site}/{user}/{name}
546-
isBare := false
547-
r, err := git.PlainClone(fullpath, isBare, &git.CloneOptions{
548-
URL: pathutil.CloneURL(reposPath),
549-
RecurseSubmodules: 10,
550-
})
551-
if err != nil {
552-
return err
553-
}
554-
555-
return gitutil.SetUpstreamRemote(r, "origin")
540+
return cmd.gitClone(pathutil.CloneURL(reposPath), fullpath, cfg)
556541
}
557542

558543
func (cmd *getCmd) fetchPlugconf(reposPath pathutil.ReposPath) error {
@@ -616,3 +601,111 @@ func (*getCmd) updateReposVersion(lockJSON *lockjson.LockJSON, reposPath pathuti
616601
}
617602
return added
618603
}
604+
605+
func (cmd *getCmd) gitFetch(r *git.Repository, workDir string, remote string, cfg *config.Config) error {
606+
err := r.Fetch(&git.FetchOptions{
607+
RemoteName: remote,
608+
})
609+
if err == nil || err == git.NoErrAlreadyUpToDate {
610+
return err
611+
}
612+
613+
// When fallback_git_cmd is true and git command is installed,
614+
// try to invoke git-fetch command
615+
if !*cfg.Get.FallbackGitCmd || !cmd.hasGitCmd() {
616+
return err
617+
}
618+
logger.Warnf("failed to fetch, try to execute \"git fetch %s\" instead...: %s", remote, err.Error())
619+
620+
before, err := gitutil.GetHEADRepository(r)
621+
fetch := exec.Command("git", "fetch", remote)
622+
fetch.Dir = workDir
623+
err = fetch.Run()
624+
if err != nil {
625+
return err
626+
}
627+
if changed, err := cmd.getWorktreeChanges(r, before); err != nil {
628+
return err
629+
} else if !changed {
630+
return git.NoErrAlreadyUpToDate
631+
}
632+
return nil
633+
}
634+
635+
func (cmd *getCmd) gitPull(r *git.Repository, workDir string, remote string, cfg *config.Config) error {
636+
wt, err := r.Worktree()
637+
if err != nil {
638+
return err
639+
}
640+
err = wt.Pull(&git.PullOptions{
641+
RemoteName: remote,
642+
RecurseSubmodules: 10,
643+
})
644+
if err == nil || err == git.NoErrAlreadyUpToDate {
645+
return err
646+
}
647+
648+
// When fallback_git_cmd is true and git command is installed,
649+
// try to invoke git-pull command
650+
if !*cfg.Get.FallbackGitCmd || !cmd.hasGitCmd() {
651+
return err
652+
}
653+
logger.Warnf("failed to pull, try to execute \"git pull\" instead...: %s", err.Error())
654+
655+
before, err := gitutil.GetHEADRepository(r)
656+
pull := exec.Command("git", "pull")
657+
pull.Dir = workDir
658+
err = pull.Run()
659+
if err != nil {
660+
return err
661+
}
662+
if changed, err := cmd.getWorktreeChanges(r, before); err != nil {
663+
return err
664+
} else if !changed {
665+
return git.NoErrAlreadyUpToDate
666+
}
667+
return nil
668+
}
669+
670+
func (cmd *getCmd) getWorktreeChanges(r *git.Repository, before string) (bool, error) {
671+
after, err := gitutil.GetHEADRepository(r)
672+
if err != nil {
673+
return false, err
674+
}
675+
return before != after, nil
676+
}
677+
678+
func (cmd *getCmd) gitClone(cloneURL, dstDir string, cfg *config.Config) error {
679+
isBare := false
680+
r, err := git.PlainClone(dstDir, isBare, &git.CloneOptions{
681+
URL: cloneURL,
682+
RecurseSubmodules: 10,
683+
})
684+
if err != nil {
685+
// When fallback_git_cmd is true and git command is installed,
686+
// try to invoke git-clone command
687+
if !*cfg.Get.FallbackGitCmd || !cmd.hasGitCmd() {
688+
return err
689+
}
690+
logger.Warnf("failed to clone, try to execute \"git clone --recursive %s %s\" instead...: %s", cloneURL, dstDir, err.Error())
691+
err = os.RemoveAll(dstDir)
692+
if err != nil {
693+
return err
694+
}
695+
out, err := exec.Command("git", "clone", "--recursive", cloneURL, dstDir).CombinedOutput()
696+
if err != nil {
697+
return fmt.Errorf("\"git clone --recursive %s %s\" failed, out=%s: %s", cloneURL, dstDir, string(out), err.Error())
698+
}
699+
}
700+
701+
return gitutil.SetUpstreamRemote(r, "origin")
702+
}
703+
704+
func (cmd *getCmd) hasGitCmd() bool {
705+
exeName := "git"
706+
if runtime.GOOS == "windows" {
707+
exeName = "git.exe"
708+
}
709+
_, err := exec.LookPath(exeName)
710+
return err == nil
711+
}

config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type ConfigBuild struct {
1818

1919
type ConfigGet struct {
2020
CreateSkeletonPlugconf *bool `toml:"create_skeleton_plugconf"`
21+
FallbackGitCmd *bool `toml:"fallback_git_cmd"`
2122
}
2223

2324
const (
@@ -33,6 +34,7 @@ func initialConfigTOML() *Config {
3334
},
3435
Get: ConfigGet{
3536
CreateSkeletonPlugconf: &trueValue,
37+
FallbackGitCmd: &trueValue,
3638
},
3739
}
3840
}
@@ -63,6 +65,9 @@ func merge(cfg, initCfg *Config) {
6365
if cfg.Get.CreateSkeletonPlugconf == nil {
6466
cfg.Get.CreateSkeletonPlugconf = initCfg.Get.CreateSkeletonPlugconf
6567
}
68+
if cfg.Get.FallbackGitCmd == nil {
69+
cfg.Get.FallbackGitCmd = initCfg.Get.FallbackGitCmd
70+
}
6671
}
6772

6873
func validate(cfg *Config) error {

0 commit comments

Comments
 (0)