11package discovery
22
33import (
4- "crypto/sha256"
5- "encoding/hex"
6- "encoding/json"
74 "errors"
8- "fmt"
9- "io/ioutil"
10- "net/http"
11- "os"
125 "os/exec"
13- "path"
14- "runtime"
15- "sort"
166 "strings"
17- "time"
187
198 "github.com/Masterminds/semver/v3"
20- "github.com/cavaliergopher/grab/v3"
219 "github.com/go-semantic-release/semantic-release/v2/pkg/analyzer"
2210 "github.com/go-semantic-release/semantic-release/v2/pkg/condition"
2311 "github.com/go-semantic-release/semantic-release/v2/pkg/config"
@@ -26,56 +14,8 @@ import (
2614 "github.com/go-semantic-release/semantic-release/v2/pkg/plugin"
2715 "github.com/go-semantic-release/semantic-release/v2/pkg/provider"
2816 "github.com/go-semantic-release/semantic-release/v2/pkg/updater"
29- "github.com/schollz/progressbar/v3"
3017)
3118
32- const PluginDir = ".semrel"
33- const PluginAPI = "https://plugins.go-semantic-release.xyz/api/v1"
34-
35- var osArchDir = runtime .GOOS + "_" + runtime .GOARCH
36-
37- func getPluginPath (name string ) string {
38- pElem := append ([]string {PluginDir }, osArchDir , name )
39- return path .Join (pElem ... )
40- }
41-
42- func ensurePluginDir (pth string ) error {
43- _ , err := os .Stat (pth )
44- if os .IsNotExist (err ) {
45- return os .MkdirAll (pth , 0755 )
46- }
47- return err
48- }
49-
50- type apiPluginAsset struct {
51- FileName string
52- URL string
53- OS string
54- Arch string
55- Checksum string
56- }
57-
58- type apiPluginRelease struct {
59- CreatedAt time.Time
60- Assets []* apiPluginAsset
61- }
62-
63- func (r * apiPluginRelease ) getMatchingAsset () * apiPluginAsset {
64- for _ , a := range r .Assets {
65- if a .OS == runtime .GOOS && a .Arch == runtime .GOARCH {
66- return a
67- }
68- }
69- return nil
70- }
71-
72- type apiPlugin struct {
73- Type string
74- Name string
75- LatestRelease string
76- Versions map [string ]* apiPluginRelease
77- }
78-
7919type Discovery struct {
8020 config * config.Config
8121}
@@ -84,178 +24,6 @@ func New(config *config.Config) (*Discovery, error) {
8424 return & Discovery {config }, nil
8525}
8626
87- func (d * Discovery ) getPluginInfo (name string ) (* apiPlugin , error ) {
88- res , err := http .Get (fmt .Sprintf ("%s/plugins/%s.json" , PluginAPI , name ))
89- if err != nil {
90- return nil , err
91- }
92- defer res .Body .Close ()
93- if res .StatusCode == 404 {
94- return nil , fmt .Errorf ("plugin not found: %s" , name )
95- }
96- if res .StatusCode < 200 || res .StatusCode >= 300 {
97- return nil , errors .New ("invalid response" )
98- }
99- var plugin * apiPlugin
100- if err := json .NewDecoder (res .Body ).Decode (& plugin ); err != nil {
101- return nil , err
102- }
103- return plugin , nil
104- }
105-
106- func showDownloadProgressBar (name string , res * grab.Response ) {
107- bar := progressbar .NewOptions64 (
108- res .Size (),
109- progressbar .OptionSetDescription (name ),
110- progressbar .OptionSetWriter (os .Stderr ),
111- progressbar .OptionShowBytes (true ),
112- progressbar .OptionSetWidth (10 ),
113- progressbar .OptionThrottle (65 * time .Millisecond ),
114- progressbar .OptionShowCount (),
115- progressbar .OptionSetWidth (40 ),
116- progressbar .OptionClearOnFinish (),
117- progressbar .OptionSetPredictTime (false ),
118- )
119- t := time .NewTicker (100 * time .Millisecond )
120- done := make (chan struct {})
121- go func () {
122- for {
123- select {
124- case <- t .C :
125- _ = bar .Set64 (res .BytesComplete ())
126- case <- res .Done :
127- _ = bar .Finish ()
128- t .Stop ()
129- done <- struct {}{}
130- return
131- }
132- }
133- }()
134- <- done
135- }
136-
137- func (d * Discovery ) fetchPlugin (name , pth string , cons * semver.Constraints ) (string , error ) {
138- pluginInfo , err := d .getPluginInfo (name )
139- if err != nil {
140- return "" , err
141- }
142-
143- foundVersion := ""
144- if cons == nil {
145- foundVersion = pluginInfo .LatestRelease
146- } else {
147- versions := make (semver.Collection , 0 )
148- for v := range pluginInfo .Versions {
149- pv , err := semver .NewVersion (v )
150- if err != nil {
151- return "" , err
152- }
153- versions = append (versions , pv )
154- }
155- sort .Sort (sort .Reverse (versions ))
156- for _ , v := range versions {
157- if cons .Check (v ) {
158- foundVersion = v .String ()
159- break
160- }
161- }
162- }
163-
164- if foundVersion == "" {
165- return "" , errors .New ("version not found" )
166- }
167-
168- releaseAsset := pluginInfo .Versions [foundVersion ].getMatchingAsset ()
169- if releaseAsset == nil {
170- return "" , fmt .Errorf ("a matching plugin was not found for %s/%s" , runtime .GOOS , runtime .GOARCH )
171- }
172-
173- targetPath := path .Join (pth , foundVersion , releaseAsset .FileName )
174-
175- req , err := grab .NewRequest (targetPath , releaseAsset .URL )
176- if err != nil {
177- return "" , err
178- }
179- if releaseAsset .Checksum != "" {
180- sum , err := hex .DecodeString (releaseAsset .Checksum )
181- if err != nil {
182- return "" , err
183- }
184- req .SetChecksum (sha256 .New (), sum , true )
185- }
186-
187- res := grab .DefaultClient .Do (req )
188- if d .config .ShowProgress {
189- showDownloadProgressBar (name , res )
190- }
191- if err := res .Err (); err != nil {
192- return "" , err
193- }
194- if err := os .Chmod (res .Filename , 0755 ); err != nil {
195- return "" , err
196- }
197-
198- return res .Filename , nil
199- }
200-
201- func getMatchingVersionDir (pth string , cons * semver.Constraints ) (string , error ) {
202- vDirs , err := ioutil .ReadDir (pth )
203- if err != nil {
204- return "" , err
205- }
206- foundVers := make (semver.Collection , 0 )
207- for _ , f := range vDirs {
208- if f .IsDir () {
209- fVer , err := semver .NewVersion (f .Name ())
210- if err != nil {
211- continue
212- }
213- foundVers = append (foundVers , fVer )
214- }
215- }
216-
217- if len (foundVers ) == 0 {
218- return "" , errors .New ("no installed version found" )
219- }
220- sort .Sort (sort .Reverse (foundVers ))
221-
222- if cons == nil {
223- return path .Join (pth , foundVers [0 ].String ()), nil
224- }
225-
226- for _ , v := range foundVers {
227- if cons .Check (v ) {
228- return path .Join (pth , v .String ()), nil
229- }
230- }
231- return "" , errors .New ("no matching version found" )
232- }
233-
234- func (d * Discovery ) findPluginLocally (pth string , cons * semver.Constraints ) (string , error ) {
235- vPth , err := getMatchingVersionDir (pth , cons )
236- if err != nil {
237- return "" , err
238- }
239-
240- files , err := ioutil .ReadDir (vPth )
241- if err != nil {
242- return "" , err
243- }
244- if len (files ) == 0 {
245- return "" , errors .New ("no plugins found" )
246- }
247- for _ , f := range files {
248- if f .IsDir () {
249- continue
250- }
251- if f .Mode ()& 0100 == 0 {
252- continue
253- }
254- return path .Join (vPth , f .Name ()), nil
255- }
256- return "" , errors .New ("no matching plugin found" )
257- }
258-
25927func getPluginType (t string ) string {
26028 switch t {
26129 case analyzer .CommitAnalyzerPluginName :
@@ -296,9 +64,9 @@ func (d *Discovery) FindPlugin(t, name string) (*plugin.PluginOpts, error) {
29664 return nil , err
29765 }
29866
299- binPath , err := d . findPluginLocally (pPath , cons )
67+ binPath , err := findPluginLocally (pPath , cons )
30068 if err != nil {
301- binPath , err = d . fetchPlugin (pName , pPath , cons )
69+ binPath , err = fetchPlugin (pName , pPath , cons , d . config . ShowProgress )
30270 if err != nil {
30371 return nil , err
30472 }
0 commit comments