Skip to content

Commit f61374a

Browse files
feat: create plugin resolve system
1 parent 4afe3b1 commit f61374a

File tree

12 files changed

+310
-161
lines changed

12 files changed

+310
-161
lines changed

pkg/plugin/client.go

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,6 @@ import (
1414
"github.com/hashicorp/go-plugin"
1515
)
1616

17-
type PluginOpts struct {
18-
Type string
19-
PluginName string
20-
Cmd *exec.Cmd
21-
}
22-
2317
var runningClientsMx sync.Mutex
2418
var runningClients = make([]*plugin.Client, 0)
2519

@@ -31,11 +25,11 @@ func KillAllPlugins() {
3125
}
3226
}
3327

34-
func StartPlugin(opts *PluginOpts) (interface{}, error) {
28+
func StartPlugin(pluginInfo *PluginInfo) (interface{}, error) {
3529
runningClientsMx.Lock()
3630
defer runningClientsMx.Unlock()
3731
logR, logW := io.Pipe()
38-
pluginLogger := log.New(os.Stderr, fmt.Sprintf("[%s]: ", opts.PluginName), 0)
32+
pluginLogger := log.New(os.Stderr, fmt.Sprintf("[%s]: ", pluginInfo.NormalizedName), 0)
3933
go func() {
4034
logLineScanner := bufio.NewScanner(logR)
4135
for logLineScanner.Scan() {
@@ -47,16 +41,20 @@ func StartPlugin(opts *PluginOpts) (interface{}, error) {
4741
pluginLogger.Println(line)
4842
}
4943
}()
44+
45+
cmd := exec.Command(pluginInfo.BinPath)
46+
cmd.SysProcAttr = GetSysProcAttr()
47+
5048
client := plugin.NewClient(&plugin.ClientConfig{
5149
HandshakeConfig: Handshake,
5250
VersionedPlugins: map[int]plugin.PluginSet{
5351
1: {
54-
opts.Type: &GRPCWrapper{
55-
Type: opts.Type,
52+
pluginInfo.Type: &GRPCWrapper{
53+
Type: pluginInfo.Type,
5654
},
5755
},
5856
},
59-
Cmd: opts.Cmd,
57+
Cmd: cmd,
6058
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
6159
Logger: hclog.NewNullLogger(),
6260
Stderr: logW,
@@ -67,7 +65,7 @@ func StartPlugin(opts *PluginOpts) (interface{}, error) {
6765
client.Kill()
6866
return nil, err
6967
}
70-
raw, err := rpcClient.Dispense(opts.Type)
68+
raw, err := rpcClient.Dispense(pluginInfo.Type)
7169
if err != nil {
7270
client.Kill()
7371
return nil, err

pkg/plugin/discovery/discovery.go

Lines changed: 35 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,82 +2,62 @@ package discovery
22

33
import (
44
"errors"
5-
"os/exec"
6-
"strings"
5+
"fmt"
76

8-
"github.com/Masterminds/semver/v3"
9-
"github.com/go-semantic-release/semantic-release/v2/pkg/analyzer"
10-
"github.com/go-semantic-release/semantic-release/v2/pkg/condition"
117
"github.com/go-semantic-release/semantic-release/v2/pkg/config"
12-
"github.com/go-semantic-release/semantic-release/v2/pkg/generator"
13-
"github.com/go-semantic-release/semantic-release/v2/pkg/hooks"
148
"github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
15-
"github.com/go-semantic-release/semantic-release/v2/pkg/provider"
16-
"github.com/go-semantic-release/semantic-release/v2/pkg/updater"
9+
"github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver"
10+
"github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver/registry"
1711
)
1812

1913
type Discovery struct {
20-
config *config.Config
14+
config *config.Config
15+
resolvers map[string]resolver.Resolver
2116
}
2217

2318
func New(config *config.Config) (*Discovery, error) {
24-
return &Discovery{config}, nil
19+
registryResolver := registry.NewResolver()
20+
return &Discovery{
21+
config: config,
22+
resolvers: map[string]resolver.Resolver{
23+
"default": registryResolver,
24+
"registry": registryResolver,
25+
},
26+
}, nil
2527
}
2628

27-
func getPluginType(t string) string {
28-
switch t {
29-
case analyzer.CommitAnalyzerPluginName:
30-
return "commit-analyzer"
31-
case condition.CIConditionPluginName:
32-
return "condition"
33-
case generator.ChangelogGeneratorPluginName:
34-
return "changelog-generator"
35-
case provider.PluginName:
36-
return "provider"
37-
case updater.FilesUpdaterPluginName:
38-
return "files-updater"
39-
case hooks.PluginName:
40-
return "hooks"
29+
func (d *Discovery) fetchPlugin(pluginInfo *plugin.PluginInfo) (string, error) {
30+
pluginResolver, ok := d.resolvers[pluginInfo.Resolver]
31+
if !ok {
32+
return "", fmt.Errorf("resolver %s not found", pluginInfo.Resolver)
4133
}
42-
return ""
43-
}
4434

45-
func (d *Discovery) FindPlugin(t, name string) (*plugin.PluginOpts, error) {
46-
pType := getPluginType(t)
47-
if pType == "" {
48-
return nil, errors.New("invalid plugin type")
35+
downloadInfo, err := pluginResolver.ResolvePlugin(pluginInfo)
36+
if err != nil {
37+
return "", err
4938
}
5039

51-
var cons *semver.Constraints
52-
if ve := strings.SplitN(name, "@", 2); len(ve) > 1 {
53-
v, err := semver.NewConstraint(ve[1])
54-
if err != nil {
55-
return nil, err
56-
}
57-
name = ve[0]
58-
cons = v
59-
}
40+
return downloadPlugin(pluginInfo, downloadInfo, d.config.ShowProgress)
41+
}
6042

61-
pName := strings.ToLower(pType + "-" + name)
62-
pPath := getPluginPath(pName)
63-
if err := ensurePluginDir(pPath); err != nil {
43+
func (d *Discovery) FindPlugin(t, name string) (*plugin.PluginInfo, error) {
44+
pInfo, err := plugin.GetPluginInfo(t, name)
45+
if err != nil {
46+
return nil, err
47+
}
48+
if err := setAndEnsurePluginPath(pInfo); err != nil {
6449
return nil, err
6550
}
6651

67-
binPath, err := findPluginLocally(pPath, cons)
68-
if err != nil {
69-
binPath, err = fetchPlugin(pName, pPath, cons, d.config.ShowProgress)
52+
binPath, err := findPluginLocally(pInfo)
53+
if errors.Is(err, ErrPluginNotFound) {
54+
binPath, err = d.fetchPlugin(pInfo)
7055
if err != nil {
7156
return nil, err
7257
}
58+
} else if err != nil {
59+
return nil, err
7360
}
74-
75-
cmd := exec.Command(binPath)
76-
cmd.SysProcAttr = GetSysProcAttr()
77-
78-
return &plugin.PluginOpts{
79-
Type: t,
80-
PluginName: pName,
81-
Cmd: cmd,
82-
}, nil
61+
pInfo.BinPath = binPath
62+
return pInfo, nil
8363
}

pkg/plugin/discovery/download.go

Lines changed: 8 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,13 @@ package discovery
33
import (
44
"crypto/sha256"
55
"encoding/hex"
6-
"errors"
7-
"fmt"
86
"os"
97
"path"
10-
"runtime"
11-
"sort"
128
"time"
139

14-
"github.com/Masterminds/semver/v3"
1510
"github.com/cavaliergopher/grab/v3"
11+
"github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
12+
"github.com/go-semantic-release/semantic-release/v2/pkg/plugin/discovery/resolver"
1613
"github.com/schollz/progressbar/v3"
1714
)
1815

@@ -47,13 +44,14 @@ func showDownloadProgressBar(name string, res *grab.Response) {
4744
<-done
4845
}
4946

50-
func downloadPlugin(name, targetPath, downloadUrl, checksum string, showProgress bool) (string, error) {
51-
req, err := grab.NewRequest(targetPath, downloadUrl)
47+
func downloadPlugin(pluginInfo *plugin.PluginInfo, downloadInfo *resolver.PluginDownloadInfo, showProgress bool) (string, error) {
48+
targetPath := path.Join(pluginInfo.PluginPath, downloadInfo.Version, downloadInfo.FileName)
49+
req, err := grab.NewRequest(targetPath, downloadInfo.URL)
5250
if err != nil {
5351
return "", err
5452
}
55-
if checksum != "" {
56-
sum, err := hex.DecodeString(checksum)
53+
if downloadInfo.Checksum != "" {
54+
sum, err := hex.DecodeString(downloadInfo.Checksum)
5755
if err != nil {
5856
return "", err
5957
}
@@ -62,7 +60,7 @@ func downloadPlugin(name, targetPath, downloadUrl, checksum string, showProgress
6260

6361
res := grab.DefaultClient.Do(req)
6462
if showProgress {
65-
showDownloadProgressBar(name, res)
63+
showDownloadProgressBar(pluginInfo.NormalizedName, res)
6664
}
6765
if err := res.Err(); err != nil {
6866
return "", err
@@ -72,43 +70,3 @@ func downloadPlugin(name, targetPath, downloadUrl, checksum string, showProgress
7270
}
7371
return res.Filename, nil
7472
}
75-
76-
func fetchPlugin(name, pth string, cons *semver.Constraints, showProgress bool) (string, error) {
77-
pluginInfo, err := getPluginInfo(name)
78-
if err != nil {
79-
return "", err
80-
}
81-
82-
foundVersion := ""
83-
if cons == nil {
84-
foundVersion = pluginInfo.LatestRelease
85-
} else {
86-
versions := make(semver.Collection, 0)
87-
for v := range pluginInfo.Versions {
88-
pv, err := semver.NewVersion(v)
89-
if err != nil {
90-
return "", err
91-
}
92-
versions = append(versions, pv)
93-
}
94-
sort.Sort(sort.Reverse(versions))
95-
for _, v := range versions {
96-
if cons.Check(v) {
97-
foundVersion = v.String()
98-
break
99-
}
100-
}
101-
}
102-
103-
if foundVersion == "" {
104-
return "", errors.New("version not found")
105-
}
106-
107-
releaseAsset := pluginInfo.Versions[foundVersion].getMatchingAsset()
108-
if releaseAsset == nil {
109-
return "", fmt.Errorf("a matching plugin was not found for %s/%s", runtime.GOOS, runtime.GOARCH)
110-
}
111-
112-
targetPath := path.Join(pth, foundVersion, releaseAsset.FileName)
113-
return downloadPlugin(name, targetPath, releaseAsset.URL, releaseAsset.Checksum, showProgress)
114-
}

pkg/plugin/discovery/local.go

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,71 +8,79 @@ import (
88
"runtime"
99
"sort"
1010

11+
"github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
12+
1113
"github.com/Masterminds/semver/v3"
1214
)
1315

1416
const PluginDir = ".semrel"
1517

1618
var osArchDir = runtime.GOOS + "_" + runtime.GOARCH
1719

18-
func getPluginPath(name string) string {
19-
pElem := append([]string{PluginDir}, osArchDir, name)
20-
return path.Join(pElem...)
21-
}
22-
23-
func ensurePluginDir(pth string) error {
24-
_, err := os.Stat(pth)
25-
if os.IsNotExist(err) {
26-
return os.MkdirAll(pth, 0755)
20+
func setAndEnsurePluginPath(pluginInfo *plugin.PluginInfo) error {
21+
pluginPath := path.Join(PluginDir, osArchDir, pluginInfo.NormalizedName)
22+
if _, err := os.Stat(pluginPath); os.IsNotExist(err) {
23+
if err := os.MkdirAll(pluginPath, 0755); err != nil {
24+
return err
25+
}
26+
} else if err != nil {
27+
return err
2728
}
28-
return err
29+
pluginInfo.PluginPath = pluginPath
30+
return nil
2931
}
3032

31-
func getMatchingVersionDir(pth string, cons *semver.Constraints) (string, error) {
32-
vDirs, err := ioutil.ReadDir(pth)
33+
var ErrPluginNotFound = errors.New("no plugin was found")
34+
35+
func getMatchingVersionDir(pluginInfo *plugin.PluginInfo) (string, error) {
36+
vDirs, err := ioutil.ReadDir(pluginInfo.PluginPath)
3337
if err != nil {
3438
return "", err
3539
}
36-
foundVers := make(semver.Collection, 0)
40+
foundVersions := make(semver.Collection, 0)
3741
for _, f := range vDirs {
3842
if f.IsDir() {
3943
fVer, err := semver.NewVersion(f.Name())
4044
if err != nil {
4145
continue
4246
}
43-
foundVers = append(foundVers, fVer)
47+
foundVersions = append(foundVersions, fVer)
4448
}
4549
}
4650

47-
if len(foundVers) == 0 {
48-
return "", errors.New("no installed version found")
51+
if len(foundVersions) == 0 {
52+
return "", nil
4953
}
50-
sort.Sort(sort.Reverse(foundVers))
54+
sort.Sort(sort.Reverse(foundVersions))
5155

52-
if cons == nil {
53-
return path.Join(pth, foundVers[0].String()), nil
56+
if pluginInfo.Constraint == nil {
57+
return path.Join(pluginInfo.PluginPath, foundVersions[0].String()), nil
5458
}
5559

56-
for _, v := range foundVers {
57-
if cons.Check(v) {
58-
return path.Join(pth, v.String()), nil
60+
for _, v := range foundVersions {
61+
if pluginInfo.Constraint.Check(v) {
62+
return path.Join(pluginInfo.PluginPath, v.String()), nil
5963
}
6064
}
61-
return "", errors.New("no matching version found")
65+
return "", nil
6266
}
6367

64-
func findPluginLocally(pth string, cons *semver.Constraints) (string, error) {
65-
vPth, err := getMatchingVersionDir(pth, cons)
68+
func findPluginLocally(pluginInfo *plugin.PluginInfo) (string, error) {
69+
vPth, err := getMatchingVersionDir(pluginInfo)
6670
if err != nil {
6771
return "", err
6872
}
6973

74+
if vPth == "" {
75+
return "", ErrPluginNotFound
76+
}
77+
7078
files, err := ioutil.ReadDir(vPth)
7179
if err != nil {
7280
return "", err
7381
}
7482
if len(files) == 0 {
75-
return "", errors.New("no plugins found")
83+
return "", ErrPluginNotFound
7684
}
7785
for _, f := range files {
7886
if f.IsDir() {
@@ -83,5 +91,5 @@ func findPluginLocally(pth string, cons *semver.Constraints) (string, error) {
8391
}
8492
return path.Join(vPth, f.Name()), nil
8593
}
86-
return "", errors.New("no matching plugin found")
94+
return "", ErrPluginNotFound
8795
}

pkg/plugin/discovery/registry.go renamed to pkg/plugin/discovery/resolver/registry/api.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package discovery
1+
package registry
22

33
import (
44
"encoding/json"
@@ -34,8 +34,6 @@ func (r *apiPluginRelease) getMatchingAsset() *apiPluginAsset {
3434
}
3535

3636
type apiPlugin struct {
37-
Type string
38-
Name string
3937
LatestRelease string
4038
Versions map[string]*apiPluginRelease
4139
}

0 commit comments

Comments
 (0)