github.com/motemen/ghq@v1.0.3/cmd_get.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"runtime"
    11  	"strings"
    12  	"sync"
    13  
    14  	"github.com/mattn/go-isatty"
    15  	"github.com/motemen/ghq/cmdutil"
    16  	"github.com/motemen/ghq/logger"
    17  	"github.com/urfave/cli/v2"
    18  	"golang.org/x/sync/errgroup"
    19  )
    20  
    21  func doGet(c *cli.Context) error {
    22  	var (
    23  		args     = c.Args().Slice()
    24  		andLook  = c.Bool("look")
    25  		parallel = c.Bool("parallel")
    26  	)
    27  	g := &getter{
    28  		update:    c.Bool("update"),
    29  		shallow:   c.Bool("shallow"),
    30  		ssh:       c.Bool("p"),
    31  		vcs:       c.String("vcs"),
    32  		silent:    c.Bool("silent"),
    33  		branch:    c.String("branch"),
    34  		recursive: !c.Bool("no-recursive"),
    35  	}
    36  	if parallel {
    37  		// force silent in parallel import
    38  		g.silent = true
    39  	}
    40  
    41  	var (
    42  		firstArg string
    43  		scr      scanner
    44  	)
    45  	if len(args) > 0 {
    46  		scr = &sliceScanner{slice: args}
    47  	} else {
    48  		fd := os.Stdin.Fd()
    49  		if isatty.IsTerminal(fd) || isatty.IsCygwinTerminal(fd) {
    50  			return fmt.Errorf("no target args specified. see `ghq get -h` for more details")
    51  		}
    52  		scr = bufio.NewScanner(os.Stdin)
    53  	}
    54  	eg := &errgroup.Group{}
    55  	sem := make(chan struct{}, 6)
    56  	for scr.Scan() {
    57  		target := scr.Text()
    58  		if firstArg == "" {
    59  			firstArg = target
    60  		}
    61  		if parallel {
    62  			sem <- struct{}{}
    63  			eg.Go(func() error {
    64  				defer func() { <-sem }()
    65  				if err := g.get(target); err != nil {
    66  					logger.Logf("error", "faied to get %q: %s", target, err)
    67  				}
    68  				return nil
    69  			})
    70  		} else {
    71  			if err := g.get(target); err != nil {
    72  				return fmt.Errorf("failed to get %q: %w", target, err)
    73  			}
    74  		}
    75  	}
    76  	if err := scr.Err(); err != nil {
    77  		return fmt.Errorf("error occurred while reading input: %w", err)
    78  	}
    79  	if err := eg.Wait(); err != nil {
    80  		return err
    81  	}
    82  	if andLook && firstArg != "" {
    83  		return look(firstArg)
    84  	}
    85  	return nil
    86  }
    87  
    88  type sliceScanner struct {
    89  	slice []string
    90  	index int
    91  }
    92  
    93  func (s *sliceScanner) Scan() bool {
    94  	s.index++
    95  	return s.index <= len(s.slice)
    96  }
    97  
    98  func (s *sliceScanner) Text() string {
    99  	return s.slice[s.index-1]
   100  }
   101  
   102  func (s *sliceScanner) Err() error {
   103  	return nil
   104  }
   105  
   106  type scanner interface {
   107  	Scan() bool
   108  	Text() string
   109  	Err() error
   110  }
   111  
   112  func detectShell() string {
   113  	shell := os.Getenv("SHELL")
   114  	if shell != "" {
   115  		return shell
   116  	}
   117  	if runtime.GOOS == "windows" {
   118  		return os.Getenv("COMSPEC")
   119  	}
   120  	return "/bin/sh"
   121  }
   122  
   123  func look(name string) error {
   124  	var (
   125  		reposFound []*LocalRepository
   126  		mu         sync.Mutex
   127  	)
   128  	if err := walkAllLocalRepositories(func(repo *LocalRepository) {
   129  		if repo.Matches(name) {
   130  			mu.Lock()
   131  			reposFound = append(reposFound, repo)
   132  			mu.Unlock()
   133  		}
   134  	}); err != nil {
   135  		return err
   136  	}
   137  
   138  	if len(reposFound) == 0 {
   139  		if url, err := newURL(name, false, false); err == nil {
   140  			repo, err := LocalRepositoryFromURL(url)
   141  			if err != nil {
   142  				return err
   143  			}
   144  			_, err = os.Stat(repo.FullPath)
   145  
   146  			// if the directory exists
   147  			if err == nil {
   148  				reposFound = append(reposFound, repo)
   149  			}
   150  		}
   151  	}
   152  
   153  	switch len(reposFound) {
   154  	case 0:
   155  		return fmt.Errorf("No repository found")
   156  	case 1:
   157  		repo := reposFound[0]
   158  		cmd := exec.Command(detectShell())
   159  		cmd.Stdin = os.Stdin
   160  		cmd.Stdout = os.Stdout
   161  		cmd.Stderr = os.Stderr
   162  		cmd.Dir = repo.FullPath
   163  		cmd.Env = append(os.Environ(), "GHQ_LOOK="+filepath.ToSlash(repo.RelPath))
   164  		return cmdutil.RunCommand(cmd, true)
   165  	default:
   166  		b := &strings.Builder{}
   167  		b.WriteString("More than one repositories are found; Try more precise name\n")
   168  		for _, repo := range reposFound {
   169  			b.WriteString(fmt.Sprintf("       - %s\n", strings.Join(repo.PathParts, "/")))
   170  		}
   171  		return errors.New(b.String())
   172  	}
   173  }