-
Notifications
You must be signed in to change notification settings - Fork 3
/
gh.go
146 lines (131 loc) · 3.33 KB
/
gh.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package ghdl
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/beetcb/ghdl/helper/sl"
)
const (
OS = runtime.GOOS
ARCH = runtime.GOARCH
)
type GHRelease struct {
RepoPath string
TagName string
}
type APIReleaseResp struct {
Assets []APIReleaseAsset `json:"assets"`
}
type APIReleaseAsset struct {
Name string `json:"name"`
DownloadUrl string `json:"browser_download_url"`
Size int `json:"size"`
}
type AssetNamePredicate func(assetName string) bool
func (gr GHRelease) GetGHReleases(filterOff bool, assetFilter *regexp.Regexp) (*GHReleaseDl, error) {
var tag string
if gr.TagName == "" {
tag = "latest"
} else {
tag = "tags/" + gr.TagName
}
// Os-specific binaryName
binaryName := filepath.Base(gr.RepoPath) + func() string {
if runtime.GOOS == "windows" {
return ".exe"
} else {
return ""
}
}()
apiUrl := fmt.Sprint("https://api.github.com/repos/", gr.RepoPath, "/releases/", tag)
// Get releases info
req, err := http.NewRequest("GET", apiUrl, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/vnd.github.v3+json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
} else if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("requst to %v failed: %v", apiUrl, resp.Status)
}
defer resp.Body.Close()
byte, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var respJSON APIReleaseResp
if err := json.Unmarshal(byte, &respJSON); err != nil {
return nil, err
}
releaseAssets := respJSON.Assets
if len(releaseAssets) == 0 {
return nil, fmt.Errorf("no binary release found")
}
// Filter or Pick release assets
matchedAssets := func() []APIReleaseAsset {
if filterOff {
return releaseAssets
}
// The common predicate rule suits for both OS and ARCH
osArchPredicate := func(match string) AssetNamePredicate {
return func(assetName string) bool {
if strings.Contains(assetName, match) {
return true
}
if match == "amd64" {
return strings.Contains(assetName, "x64")
}
return strings.Contains(assetName, "x64_64")
}
}
predicates := []AssetNamePredicate{
osArchPredicate(OS),
osArchPredicate(ARCH),
}
if assetFilter != nil {
predicates = append(predicates, func(assetName string) bool {
return assetFilter.MatchString(assetName)
})
}
return filterAssets(releaseAssets, predicates)
}()
matchedIdx := 0
if len(matchedAssets) != 1 {
var choices []string
for _, asset := range matchedAssets {
choices = append(choices, asset.Name)
}
idx := sl.Select(&choices)
matchedIdx = idx
}
asset := matchedAssets[matchedIdx]
return &GHReleaseDl{binaryName, asset.DownloadUrl, int64(asset.Size)}, nil
}
func filterAssets(assets []APIReleaseAsset, predicates []AssetNamePredicate) []APIReleaseAsset {
ret := assets
for _, p := range predicates {
ret = filter(ret, p)
}
return ret
}
// filterAssets assets using the provided predicates, falling back to the default assets if no match is found
func filter(assets []APIReleaseAsset, predicate AssetNamePredicate) []APIReleaseAsset {
var ret []APIReleaseAsset
for _, asset := range assets {
lowerName := strings.ToLower(asset.Name)
if predicate(lowerName) {
ret = append(ret, asset)
}
}
if len(ret) == 0 {
return assets
}
return ret
}