-
Notifications
You must be signed in to change notification settings - Fork 25
/
binrepo.go
131 lines (106 loc) · 2.48 KB
/
binrepo.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
package gvm
import (
"encoding/xml"
"fmt"
"net/http"
"net/url"
"os"
"regexp"
"github.com/andrewkroh/gvm/common"
)
var reGostoreVersion = regexp.MustCompile(`go(.*)\.(.*)-(.*)\..*`)
func (m *Manager) installBinary(version *GoVersion) (string, error) {
godir := m.versionDir(version)
tmp, err := os.MkdirTemp("", godir)
if err != nil {
return "", err
}
defer os.RemoveAll(tmp)
extension := "tar.gz"
if m.GOOS == "windows" {
extension = "zip"
}
goURL := fmt.Sprintf("%s/go%v.%v-%v.%v", m.GoStorageHome, version, m.GOOS, m.GOARCH, extension)
path, err := common.DownloadFile(goURL, tmp, m.HTTPTimeout, common.DefaultRetryParams)
if err != nil {
return "", fmt.Errorf("failed downloading from %v: %w", goURL, err)
}
return extractTo(m.VersionGoROOT(version), path)
}
func (m *Manager) AvailableBinaries() ([]*GoVersion, error) {
home, goos, goarch := m.GoStorageHome, m.GOOS, m.GOARCH
versions := map[string]struct{}{}
err := m.iterXMLDirListing(home, func(name string) bool {
matches := reGostoreVersion.FindStringSubmatch(name)
if len(matches) < 4 {
return true
}
matches = matches[1:]
if matches[1] != goos || matches[2] != goarch {
return true
}
versions[matches[0]] = struct{}{}
return true
})
if err != nil {
return nil, err
}
list := make([]*GoVersion, 0, len(versions))
for version := range versions {
ver, err := ParseVersion(version)
if err != nil {
continue
}
list = append(list, ver)
}
sortVersions(list)
return list, nil
}
func (m *Manager) iterXMLDirListing(home string, fn func(entry string) bool) error {
marker := ""
client := &http.Client{
Timeout: m.HTTPTimeout,
}
for {
type contents struct {
Key string
}
listing := struct {
IsTruncated bool
NextMarker string
Contents []contents
}{}
req, err := http.NewRequest("GET", home, nil)
if err != nil {
return err
}
q := url.Values{}
q.Add("marker", marker)
req.URL.RawQuery = q.Encode()
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return fmt.Errorf("listing failed with http status %v", resp.StatusCode)
}
dec := xml.NewDecoder(resp.Body)
if err := dec.Decode(&listing); err != nil {
resp.Body.Close()
return err
}
resp.Body.Close()
for i := range listing.Contents {
cont := fn(listing.Contents[i].Key)
if !cont {
return nil
}
}
next := listing.NextMarker
if next == "" {
return nil
}
marker = next
}
}