mirror of
https://github.com/asdf-vm/asdf.git
synced 2024-12-19 09:55:01 -07:00
Merge pull request #36 from asdf-vm/tb/plugin-update-cmd
feat(golang-rewrite): create plugin update command
This commit is contained in:
commit
0aba948c65
54
cmd/cmd.go
54
cmd/cmd.go
@ -82,9 +82,15 @@ func Execute() {
|
||||
},
|
||||
{
|
||||
Name: "update",
|
||||
Action: func(_ *cli.Context) error {
|
||||
log.Print("Ipsum")
|
||||
return nil
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "all",
|
||||
Usage: "Update all installed plugins",
|
||||
},
|
||||
},
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
args := cCtx.Args()
|
||||
return pluginUpdateCommand(cCtx, logger, args.Get(0), args.Get(1))
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -170,3 +176,45 @@ func pluginListCommand(cCtx *cli.Context, logger *log.Logger) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pluginUpdateCommand(cCtx *cli.Context, logger *log.Logger, pluginName, ref string) error {
|
||||
updateAll := cCtx.Bool("all")
|
||||
if !updateAll && pluginName == "" {
|
||||
return cli.Exit("usage: asdf plugin-update {<name> [git-ref] | --all}", 1)
|
||||
}
|
||||
|
||||
conf, err := config.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Printf("error loading config: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if updateAll {
|
||||
installedPlugins, err := plugins.List(conf, false, false)
|
||||
if err != nil {
|
||||
logger.Printf("failed to get plugin list: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, plugin := range installedPlugins {
|
||||
updatedToRef, err := plugins.Update(conf, plugin.Name, "")
|
||||
formatUpdateResult(logger, plugin.Name, updatedToRef, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
updatedToRef, err := plugins.Update(conf, pluginName, ref)
|
||||
formatUpdateResult(logger, pluginName, updatedToRef, err)
|
||||
return err
|
||||
}
|
||||
|
||||
func formatUpdateResult(logger *log.Logger, pluginName, updatedToRef string, err error) {
|
||||
if err != nil {
|
||||
logger.Printf("failed to update %s due to error: %s\n", pluginName, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
logger.Printf("updated %s to ref %s\n", pluginName, updatedToRef)
|
||||
}
|
||||
|
@ -9,8 +9,7 @@ import (
|
||||
"regexp"
|
||||
|
||||
"asdf/config"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"asdf/plugins/git"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -19,7 +18,9 @@ const (
|
||||
pluginAlreadyExists = "plugin named %q already added"
|
||||
)
|
||||
|
||||
// Plugin represents a plugin to the packages in asdf
|
||||
// Plugin struct represents an asdf plugin to all asdf code. The name and dir
|
||||
// fields are the most used fields. Ref and Dir only still git info, which is
|
||||
// only information and shown to the user at times.
|
||||
type Plugin struct {
|
||||
Name string
|
||||
Dir string
|
||||
@ -42,25 +43,22 @@ func List(config config.Config, urls, refs bool) (plugins []Plugin, err error) {
|
||||
var url string
|
||||
var refString string
|
||||
location := filepath.Join(pluginsDir, file.Name())
|
||||
repo, err := git.PlainOpen(location)
|
||||
plugin := git.NewPlugin(location)
|
||||
|
||||
// TODO: Improve these error messages
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
|
||||
if refs {
|
||||
ref, err := repo.Head()
|
||||
refString = ref.Hash().String()
|
||||
|
||||
refString, err = plugin.Head()
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
}
|
||||
|
||||
if urls {
|
||||
remotes, err := repo.Remotes()
|
||||
url = remotes[0].Config().URLs[0]
|
||||
|
||||
url, err = plugin.RemoteURL()
|
||||
if err != nil {
|
||||
return plugins, err
|
||||
}
|
||||
@ -107,17 +105,10 @@ func Add(config config.Config, pluginName, pluginURL string) error {
|
||||
return fmt.Errorf("unable to create plugin directory: %w", err)
|
||||
}
|
||||
|
||||
_, err = git.PlainClone(pluginDir, false, &git.CloneOptions{
|
||||
URL: pluginURL,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to clone plugin: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return git.NewPlugin(pluginDir).Clone(pluginURL)
|
||||
}
|
||||
|
||||
// Remove removes a plugin with the provided name if installed
|
||||
// Remove uninstalls a plugin by removing it from the file system if installed
|
||||
func Remove(config config.Config, pluginName string) error {
|
||||
err := validatePluginName(pluginName)
|
||||
if err != nil {
|
||||
@ -138,11 +129,33 @@ func Remove(config config.Config, pluginName string) error {
|
||||
return os.RemoveAll(pluginDir)
|
||||
}
|
||||
|
||||
// Update a plugin to a specific ref, or if no ref provided update to latest
|
||||
func Update(config config.Config, pluginName, ref string) (string, error) {
|
||||
exists, err := PluginExists(config.DataDir, pluginName)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to check if plugin exists: %w", err)
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return "", fmt.Errorf("no such plugin: %s", pluginName)
|
||||
}
|
||||
|
||||
pluginDir := PluginDirectory(config.DataDir, pluginName)
|
||||
|
||||
plugin := git.NewPlugin(pluginDir)
|
||||
|
||||
return plugin.Update(ref)
|
||||
}
|
||||
|
||||
// PluginExists returns a boolean indicating whether or not a plugin with the
|
||||
// provided name is currently installed
|
||||
func PluginExists(dataDir, pluginName string) (bool, error) {
|
||||
pluginDir := PluginDirectory(dataDir, pluginName)
|
||||
fileInfo, err := os.Stat(pluginDir)
|
||||
return directoryExists(pluginDir)
|
||||
}
|
||||
|
||||
func directoryExists(dir string) (bool, error) {
|
||||
fileInfo, err := os.Stat(dir)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
@ -154,13 +167,14 @@ func PluginExists(dataDir, pluginName string) (bool, error) {
|
||||
return fileInfo.IsDir(), nil
|
||||
}
|
||||
|
||||
// PluginDirectory returns the directory a plugin would be installed in, if it
|
||||
// is installed
|
||||
// PluginDirectory returns the directory a plugin with a given name would be in
|
||||
// if it were installed
|
||||
func PluginDirectory(dataDir, pluginName string) string {
|
||||
return filepath.Join(DataDirectory(dataDir), pluginName)
|
||||
}
|
||||
|
||||
// DataDirectory return the plugin directory inside the data directory
|
||||
// DataDirectory returns the path to the plugin directory inside the data
|
||||
// directory
|
||||
func DataDirectory(dataDir string) string {
|
||||
return filepath.Join(dataDir, dataDirPlugins)
|
||||
}
|
||||
|
@ -157,7 +157,6 @@ func TestRemove(t *testing.T) {
|
||||
t.Run("returns error when invalid plugin name is given", func(t *testing.T) {
|
||||
err := Remove(conf, "foo/bar/baz")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
expectedErrMsg := "is invalid. Name may only contain lowercase letters, numbers, '_', and '-'"
|
||||
assert.ErrorContains(t, err, expectedErrMsg)
|
||||
})
|
||||
@ -173,6 +172,72 @@ func TestRemove(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
conf := config.Config{DataDir: testDataDir}
|
||||
|
||||
err := Add(conf, testPluginName, testRepo)
|
||||
assert.Nil(t, err)
|
||||
|
||||
badPluginName := "badplugin"
|
||||
badRepo := PluginDirectory(testDataDir, badPluginName)
|
||||
err = os.MkdirAll(badRepo, 0o777)
|
||||
assert.Nil(t, err)
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
givenConf config.Config
|
||||
givenName string
|
||||
givenRef string
|
||||
wantSomeRef bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
desc: "returns error when plugin with name does not exist",
|
||||
givenConf: conf,
|
||||
givenName: "nonexistant",
|
||||
givenRef: "",
|
||||
wantSomeRef: false,
|
||||
wantErrMsg: "no such plugin: nonexistant",
|
||||
},
|
||||
{
|
||||
desc: "returns error when plugin repo does not exist",
|
||||
givenConf: conf,
|
||||
givenName: "badplugin",
|
||||
givenRef: "",
|
||||
wantSomeRef: false,
|
||||
wantErrMsg: "unable to open plugin Git repository: repository does not exist",
|
||||
},
|
||||
{
|
||||
desc: "updates plugin when plugin with name exists",
|
||||
givenConf: conf,
|
||||
givenName: testPluginName,
|
||||
givenRef: "",
|
||||
wantSomeRef: true,
|
||||
wantErrMsg: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
updatedToRef, err := Update(tt.givenConf, tt.givenName, tt.givenRef)
|
||||
|
||||
if tt.wantErrMsg == "" {
|
||||
assert.Nil(t, err)
|
||||
} else {
|
||||
assert.NotNil(t, err)
|
||||
assert.ErrorContains(t, err, tt.wantErrMsg)
|
||||
}
|
||||
|
||||
if tt.wantSomeRef == true {
|
||||
assert.NotZero(t, updatedToRef)
|
||||
} else {
|
||||
assert.Zero(t, updatedToRef)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPluginExists(t *testing.T) {
|
||||
testDataDir := t.TempDir()
|
||||
pluginDir := PluginDirectory(testDataDir, testPluginName)
|
||||
@ -305,29 +370,34 @@ func installMockPluginRepo(dataDir, name string) (string, error) {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "commit", "-q", "-m", fmt.Sprintf("\"asdf %s plugin\"", name))
|
||||
return location, err
|
||||
}
|
||||
|
||||
// helper function to make running commands easier
|
||||
func runCmd(cmdName string, args ...string) error {
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
|
||||
// Capture stdout and stderr
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err := cmd.Run()
|
||||
err = runCmd("git", "-C", location, "commit", "-q", "-m", fmt.Sprintf("\"asdf %s plugin init\"", name))
|
||||
if err != nil {
|
||||
// If command fails print both stderr and stdout
|
||||
fmt.Println("stdout:", stdout.String())
|
||||
fmt.Println("stderr:", stderr.String())
|
||||
return err
|
||||
return location, err
|
||||
}
|
||||
|
||||
return nil
|
||||
err = runCmd("touch", filepath.Join(location, "README.md"))
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "add", "-A")
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
err = runCmd("git", "-C", location, "commit", "-q", "-m", fmt.Sprintf("\"asdf %s plugin readme \"", name))
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
// kind of ugly but I want a remote with a valid path so I use the same
|
||||
// location as the remote. Probably should refactor
|
||||
err = runCmd("git", "-C", location, "remote", "add", "origin", location)
|
||||
if err != nil {
|
||||
return location, err
|
||||
}
|
||||
|
||||
return location, err
|
||||
}
|
||||
|
||||
func moduleRoot() (string, error) {
|
||||
@ -360,3 +430,24 @@ func findModuleRoot(dir string) (roots string) {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// helper function to make running commands easier
|
||||
func runCmd(cmdName string, args ...string) error {
|
||||
cmd := exec.Command(cmdName, args...)
|
||||
|
||||
// Capture stdout and stderr
|
||||
var stdout strings.Builder
|
||||
var stderr strings.Builder
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
// If command fails print both stderr and stdout
|
||||
fmt.Println("stdout:", stdout.String())
|
||||
fmt.Println("stderr:", stderr.String())
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user