121 lines
2.6 KiB
Go
121 lines
2.6 KiB
Go
package extensions
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/cli/cli/internal/config"
|
|
"github.com/cli/safeexec"
|
|
)
|
|
|
|
type Manager struct {
|
|
dataDir func() string
|
|
lookPath func(string) (string, error)
|
|
}
|
|
|
|
func NewManager() *Manager {
|
|
return &Manager{
|
|
dataDir: config.ConfigDir,
|
|
lookPath: safeexec.LookPath,
|
|
}
|
|
}
|
|
|
|
func (m *Manager) Dispatch(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error) {
|
|
if len(args) == 0 {
|
|
return false, errors.New("too few arguments in list")
|
|
}
|
|
|
|
var exe string
|
|
extName := "gh-" + args[0]
|
|
forwardArgs := args[1:]
|
|
|
|
for _, e := range m.List() {
|
|
if filepath.Base(e) == extName {
|
|
exe = e
|
|
break
|
|
}
|
|
}
|
|
if exe == "" {
|
|
return false, nil
|
|
}
|
|
|
|
// TODO: parse the shebang on Windows and invoke the correct interpreter instead of invoking directly
|
|
externalCmd := exec.Command(exe, forwardArgs...)
|
|
externalCmd.Stdin = stdin
|
|
externalCmd.Stdout = stdout
|
|
externalCmd.Stderr = stderr
|
|
return true, externalCmd.Run()
|
|
}
|
|
|
|
func (m *Manager) List() []string {
|
|
dir := m.installDir()
|
|
entries, err := ioutil.ReadDir(dir)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
var results []string
|
|
for _, f := range entries {
|
|
if !strings.HasPrefix(f.Name(), "gh-") || !(f.IsDir() || f.Mode()&os.ModeSymlink != 0) {
|
|
continue
|
|
}
|
|
results = append(results, filepath.Join(dir, f.Name(), f.Name()))
|
|
}
|
|
return results
|
|
}
|
|
|
|
func (m *Manager) InstallLocal(dir string) error {
|
|
name := filepath.Base(dir)
|
|
targetDir := filepath.Join(m.installDir(), name)
|
|
return os.Symlink(dir, targetDir)
|
|
}
|
|
|
|
func (m *Manager) Install(cloneURL string, stdout, stderr io.Writer) error {
|
|
exe, err := m.lookPath("git")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
name := strings.TrimSuffix(path.Base(cloneURL), ".git")
|
|
targetDir := filepath.Join(m.installDir(), name)
|
|
|
|
externalCmd := exec.Command(exe, "clone", cloneURL, targetDir)
|
|
externalCmd.Stdout = stdout
|
|
externalCmd.Stderr = stderr
|
|
return externalCmd.Run()
|
|
}
|
|
|
|
func (m *Manager) Upgrade(stdout, stderr io.Writer) error {
|
|
exe, err := m.lookPath("git")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
exts := m.List()
|
|
if len(exts) == 0 {
|
|
return errors.New("no extensions installed")
|
|
}
|
|
|
|
for _, f := range exts {
|
|
fmt.Fprintf(stdout, "[%s]: ", filepath.Base(f))
|
|
dir := filepath.Dir(f)
|
|
externalCmd := exec.Command(exe, "-C", dir, "--git-dir="+filepath.Join(dir, ".git"), "pull", "--ff-only")
|
|
externalCmd.Stdout = stdout
|
|
externalCmd.Stderr = stderr
|
|
if e := externalCmd.Run(); e != nil {
|
|
err = e
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (m *Manager) installDir() string {
|
|
return filepath.Join(m.dataDir(), "extensions")
|
|
}
|