]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/pluginmanager.go
Merge pull request #507 from NicolaiSoeborg/master
[micro.git] / cmd / micro / pluginmanager.go
index c9d0b7ac0dac246665342680d852917b80ef1806..0703a62f5d1d4339aeceb35728cf5281a50a1eb3 100644 (file)
@@ -14,16 +14,12 @@ import (
        "sync"
 
        "github.com/blang/semver"
-       "github.com/yosuke-furukawa/json5/encoding/json5"
        "github.com/yuin/gopher-lua"
+       "github.com/zyedidia/json5/encoding/json5"
 )
 
 var (
-       pluginChannels PluginChannels = PluginChannels{
-               PluginChannel("https://www.boombuler.de/channel.json"),
-       }
-
-       allPluginPackages PluginPackages = nil
+       allPluginPackages PluginPackages
 )
 
 // CorePluginName is a plugin dependency name for the micro core.
@@ -61,7 +57,7 @@ type PluginVersion struct {
 // PluginVersions is a slice of PluginVersion
 type PluginVersions []*PluginVersion
 
-// PluginDenendency descripes a dependency to another plugin or micro itself.
+// PluginDependency descripes a dependency to another plugin or micro itself.
 type PluginDependency struct {
        Name  string
        Range semver.Range
@@ -124,7 +120,7 @@ func (pc PluginChannels) Fetch() PluginPackages {
 
 // Fetch retrieves all available PluginPackages from the given channel
 func (pc PluginChannel) Fetch() PluginPackages {
-       messenger.AddLog(fmt.Sprintf("Fetching channel: %q", string(pc)))
+       // messenger.AddLog(fmt.Sprintf("Fetching channel: %q", string(pc)))
        resp, err := http.Get(string(pc))
        if err != nil {
                TermMessage("Failed to query plugin channel:\n", err)
@@ -145,7 +141,7 @@ func (pc PluginChannel) Fetch() PluginPackages {
 
 // Fetch retrieves all available PluginPackages from the given repository
 func (pr PluginRepository) Fetch() PluginPackages {
-       messenger.AddLog(fmt.Sprintf("Fetching repository: %q", string(pr)))
+       // messenger.AddLog(fmt.Sprintf("Fetching repository: %q", string(pr)))
        resp, err := http.Get(string(pr))
        if err != nil {
                TermMessage("Failed to query plugin repository:\n", err)
@@ -159,7 +155,11 @@ func (pr PluginRepository) Fetch() PluginPackages {
                TermMessage("Failed to decode repository data:\n", err)
                return PluginPackages{}
        }
-       return plugins
+       if len(plugins) > 0 {
+               return PluginPackages{plugins[0]}
+       }
+       return nil
+       // return plugins
 }
 
 // UnmarshalJSON unmarshals raw json to a PluginVersion
@@ -178,8 +178,13 @@ func (pv *PluginVersion) UnmarshalJSON(data []byte) error {
        pv.Require = make(PluginDependencies, 0)
 
        for k, v := range values.Require {
-               if vRange, err := semver.ParseRange(v); err == nil {
-                       pv.Require = append(pv.Require, &PluginDependency{k, vRange})
+               // don't add the dependency if it's the core and
+               // we have a unknown version number.
+               // in that case just accept that dependency (which equals to not adding it.)
+               if k != CorePluginName || !isUnknownCoreVersion() {
+                       if vRange, err := semver.ParseRange(v); err == nil {
+                               pv.Require = append(pv.Require, &PluginDependency{k, vRange})
+                       }
                }
        }
        return nil
@@ -211,7 +216,39 @@ func (pp *PluginPackage) UnmarshalJSON(data []byte) error {
 // GetAllPluginPackages gets all PluginPackages which may be available.
 func GetAllPluginPackages() PluginPackages {
        if allPluginPackages == nil {
-               allPluginPackages = pluginChannels.Fetch()
+               getOption := func(name string) []string {
+                       data := GetOption(name)
+                       if strs, ok := data.([]string); ok {
+                               return strs
+                       }
+                       if ifs, ok := data.([]interface{}); ok {
+                               result := make([]string, len(ifs))
+                               for i, urlIf := range ifs {
+                                       if url, ok := urlIf.(string); ok {
+                                               result[i] = url
+                                       } else {
+                                               return nil
+                                       }
+                               }
+                               return result
+                       }
+                       return nil
+               }
+
+               channels := PluginChannels{}
+               for _, url := range getOption("pluginchannels") {
+                       channels = append(channels, PluginChannel(url))
+               }
+               repos := []PluginRepository{}
+               for _, url := range getOption("pluginrepos") {
+                       repos = append(repos, PluginRepository(url))
+               }
+               allPluginPackages = fetchAllSources(len(repos)+1, func(i int) PluginPackages {
+                       if i == 0 {
+                               return channels.Fetch()
+                       }
+                       return repos[i-1].Fetch()
+               })
        }
        return allPluginPackages
 }
@@ -236,8 +273,8 @@ func (pv PluginVersions) Swap(i, j int) {
 }
 
 // Less returns true if the version at position i is greater then the version at position j (used for sorting)
-func (s PluginVersions) Less(i, j int) bool {
-       return s[i].Version.GT(s[j].Version)
+func (pv PluginVersions) Less(i, j int) bool {
+       return pv[i].Version.GT(pv[j].Version)
 }
 
 // Match returns true if the package matches a given search text
@@ -260,13 +297,13 @@ func (pp PluginPackage) Match(text string) bool {
 }
 
 // IsInstallable returns true if the package can be installed.
-func (pp PluginPackage) IsInstallable() bool {
+func (pp PluginPackage) IsInstallable() error {
        _, err := GetAllPluginPackages().Resolve(GetInstalledVersions(true), PluginDependencies{
                &PluginDependency{
                        Name:  pp.Name,
                        Range: semver.Range(func(v semver.Version) bool { return true }),
                }})
-       return err == nil
+       return err
 }
 
 // SearchPlugin retrieves a list of all PluginPackages which match the given search text and
@@ -282,13 +319,18 @@ pluginLoop:
                        }
                }
 
-               if pp.IsInstallable() {
+               if err := pp.IsInstallable(); err == nil {
                        plugins = append(plugins, pp)
                }
        }
        return
 }
 
+func isUnknownCoreVersion() bool {
+       _, err := semver.ParseTolerant(Version)
+       return err != nil
+}
+
 func newStaticPluginVersion(name, version string) *PluginVersion {
        vers, err := semver.ParseTolerant(version)
 
@@ -316,8 +358,8 @@ func GetInstalledVersions(withCore bool) PluginVersions {
                result = append(result, newStaticPluginVersion(CorePluginName, Version))
        }
 
-       for _, name := range loadedPlugins {
-               version := GetInstalledPluginVersion(name)
+       for name, lpname := range loadedPlugins {
+               version := GetInstalledPluginVersion(lpname)
                if pv := newStaticPluginVersion(name, version); pv != nil {
                        result = append(result, pv)
                }
@@ -339,6 +381,7 @@ func GetInstalledPluginVersion(name string) string {
        return ""
 }
 
+// DownloadAndInstall downloads and installs the given plugin and version
 func (pv *PluginVersion) DownloadAndInstall() error {
        messenger.AddLog(fmt.Sprintf("Downloading %q (%s) from %q", pv.pack.Name, pv.Version, pv.Url))
        resp, err := http.Get(pv.Url)
@@ -396,13 +439,13 @@ func (pv *PluginVersion) DownloadAndInstall() error {
                                return err
                        }
                        defer content.Close()
-                       if target, err := os.Create(targetName); err != nil {
+                       target, err := os.Create(targetName)
+                       if err != nil {
+                               return err
+                       }
+                       defer target.Close()
+                       if _, err = io.Copy(target, content); err != nil {
                                return err
-                       } else {
-                               defer target.Close()
-                               if _, err = io.Copy(target, content); err != nil {
-                                       return err
-                               }
                        }
                }
        }
@@ -452,6 +495,7 @@ func (req PluginDependencies) Join(other PluginDependencies) PluginDependencies
        return result
 }
 
+// Resolve resolves dependencies between different plugins
 func (all PluginPackages) Resolve(selectedVersions PluginVersions, open PluginDependencies) (PluginVersions, error) {
        if len(open) == 0 {
                return selectedVersions, nil
@@ -463,31 +507,29 @@ func (all PluginPackages) Resolve(selectedVersions PluginVersions, open PluginDe
                                return all.Resolve(selectedVersions, stillOpen)
                        }
                        return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name)
-               } else {
-                       availableVersions := all.GetAllVersions(currentRequirement.Name)
-                       sort.Sort(availableVersions)
+               }
+               availableVersions := all.GetAllVersions(currentRequirement.Name)
+               sort.Sort(availableVersions)
 
-                       for _, version := range availableVersions {
-                               if currentRequirement.Range(version.Version) {
-                                       resolved, err := all.Resolve(append(selectedVersions, version), stillOpen.Join(version.Require))
+               for _, version := range availableVersions {
+                       if currentRequirement.Range(version.Version) {
+                               resolved, err := all.Resolve(append(selectedVersions, version), stillOpen.Join(version.Require))
 
-                                       if err == nil {
-                                               return resolved, nil
-                                       }
+                               if err == nil {
+                                       return resolved, nil
                                }
                        }
-                       return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name)
                }
-       } else {
-               return selectedVersions, nil
+               return nil, fmt.Errorf("unable to find a matching version for \"%s\"", currentRequirement.Name)
        }
+       return selectedVersions, nil
 }
 
-func (versions PluginVersions) install() {
+func (pv PluginVersions) install() {
        anyInstalled := false
        currentlyInstalled := GetInstalledVersions(true)
 
-       for _, sel := range versions {
+       for _, sel := range pv {
                if sel.pack.Name != CorePluginName {
                        shouldInstall := true
                        if pv := currentlyInstalled.find(sel.pack.Name); pv != nil {
@@ -519,9 +561,12 @@ func (versions PluginVersions) install() {
 func UninstallPlugin(name string) {
        if err := os.RemoveAll(filepath.Join(configDir, "plugins", name)); err != nil {
                messenger.Error(err)
+               return
        }
+       delete(loadedPlugins, name)
 }
 
+// Install installs the plugin
 func (pl PluginPackage) Install() {
        selected, err := GetAllPluginPackages().Resolve(GetInstalledVersions(true), PluginDependencies{
                &PluginDependency{
@@ -535,10 +580,13 @@ func (pl PluginPackage) Install() {
        selected.install()
 }
 
+// UpdatePlugins updates the given plugins
 func UpdatePlugins(plugins []string) {
        // if no plugins are specified, update all installed plugins.
        if len(plugins) == 0 {
-               plugins = loadedPlugins
+               for name := range loadedPlugins {
+                       plugins = append(plugins, name)
+               }
        }
 
        messenger.AddLog("Checking for plugin updates")