From 8e07b08ea7ad8be2f0403adbbabd5bd654f15b2f Mon Sep 17 00:00:00 2001
From: Christoph Witzko <github@christophwitzko.com>
Date: Wed, 1 Feb 2023 22:40:41 +0100
Subject: [PATCH] feat: prefetch plugins if possible

---
 cmd/semantic-release/main.go  |  8 +++++
 go.mod                        |  5 ++--
 go.sum                        | 10 ++++---
 pkg/plugin/manager/manager.go | 56 +++++++++++++++++++++++++++++++++--
 4 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/cmd/semantic-release/main.go b/cmd/semantic-release/main.go
index d8f10c4..ec75214 100644
--- a/cmd/semantic-release/main.go
+++ b/cmd/semantic-release/main.go
@@ -107,6 +107,14 @@ func cliHandler(cmd *cobra.Command, args []string) {
 		return
 	}
 
+	ok, _, err := pluginManager.PrefetchAllPluginsIfBatchIsPossible()
+	if err != nil {
+		logger.Printf("warning: failed to prefetch plugins: %v", err)
+	}
+	if ok {
+		logger.Println("all plugins were prefetched")
+	}
+
 	ci, err := pluginManager.GetCICondition()
 	exitIfError(err)
 	ciName := ci.Name()
diff --git a/go.mod b/go.mod
index 4c4945e..b9acf5e 100644
--- a/go.mod
+++ b/go.mod
@@ -5,6 +5,7 @@ go 1.19
 require (
 	github.com/Masterminds/semver/v3 v3.2.0
 	github.com/cavaliergopher/grab/v3 v3.0.1
+	github.com/go-semantic-release/plugin-registry v1.4.0
 	github.com/google/go-github/v49 v49.1.1-0.20230111123143-ced4abd274f3
 	github.com/hashicorp/go-hclog v1.4.0
 	github.com/hashicorp/go-plugin v1.4.8
@@ -13,7 +14,7 @@ require (
 	github.com/spf13/viper v1.14.0
 	github.com/stretchr/testify v1.8.1
 	golang.org/x/oauth2 v0.4.0
-	google.golang.org/grpc v1.52.0
+	google.golang.org/grpc v1.52.3
 	google.golang.org/protobuf v1.28.1
 )
 
@@ -49,7 +50,7 @@ require (
 	golang.org/x/term v0.4.0 // indirect
 	golang.org/x/text v0.6.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
-	google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9 // indirect
+	google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index 1476936..05e8dfe 100644
--- a/go.sum
+++ b/go.sum
@@ -68,6 +68,8 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-semantic-release/plugin-registry v1.4.0 h1:hp/M6zjmv5fKIyTuqKgdvZi6WrHmXUIfswhgv4Q3xMg=
+github.com/go-semantic-release/plugin-registry v1.4.0/go.mod h1:UauFd53y1hbnb4VMCeJkMVa2j8YVNioSAnh4qByYapw=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -498,8 +500,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9 h1:ru6tJGasqJpgjM4q3Qq2fS3FKQ6CPPSRqgolUVBc994=
-google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
+google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa h1:qQPhfbPO23fwm/9lQr91L1u62Zo6cm+zI+slZT+uf+o=
+google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -516,8 +518,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
 google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk=
-google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
+google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ=
+google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go
index 042e083..0e3e743 100644
--- a/pkg/plugin/manager/manager.go
+++ b/pkg/plugin/manager/manager.go
@@ -142,12 +142,62 @@ func (m *PluginManager) getAllPlugins() [][]string {
 	return plugins
 }
 
-func (m *PluginManager) FetchAllPlugins() error {
+func (m *PluginManager) getAllPluginInfos() ([]*plugin.Info, error) {
+	infos := make([]*plugin.Info, 0, 4)
 	for _, pl := range m.getAllPlugins() {
-		_, err := m.discovery.FindPlugin(pl[0], pl[1])
+		pInfo, err := plugin.GetPluginInfo(pl[0], pl[1])
 		if err != nil {
-			return err
+			return nil, err
+		}
+		infos = append(infos, pInfo)
+	}
+	return infos, nil
+}
+
+func (m *PluginManager) checkIfSameResolvers(infos []*plugin.Info) (string, bool) {
+	resolver := ""
+	for _, info := range infos {
+		if resolver == "" {
+			resolver = info.Resolver
+		} else if resolver != info.Resolver {
+			return "", false
+		}
+	}
+	return resolver, true
+}
+
+func (m *PluginManager) FetchAllPlugins() error {
+	batchWasPossible, pInfos, err := m.PrefetchAllPluginsIfBatchIsPossible()
+	if err != nil {
+		return err
+	}
+	if batchWasPossible {
+		return nil
+	}
+
+	// try to find plugins one by one
+	for _, pInfo := range pInfos {
+		fErr := m.discovery.FindPluginByPluginInfo(pInfo)
+		if fErr != nil {
+			return fErr
 		}
 	}
 	return nil
 }
+
+func (m *PluginManager) PrefetchAllPluginsIfBatchIsPossible() (bool, []*plugin.Info, error) {
+	pInfos, err := m.getAllPluginInfos()
+	if err != nil {
+		return false, nil, err
+	}
+
+	if resolver, ok := m.checkIfSameResolvers(pInfos); ok && m.discovery.IsBatchResolver(resolver) {
+		// all plugins have the same resolver, and it supports batch resolving
+		bErr := m.discovery.FindPluginsWithBatchResolver(resolver, pInfos)
+		if bErr != nil {
+			return false, nil, bErr
+		}
+		return true, pInfos, nil
+	}
+	return false, pInfos, nil
+}
-- 
GitLab