Skip to content

Commit 22c5028

Browse files
committed
[WIP] Working download toold from json
they are now saved into {tool}/{version} dropping all extra folder levels. There is a lot of duplicate code to slim down version check is semver-compliant
1 parent d083191 commit 22c5028

File tree

1 file changed

+183
-51
lines changed

1 file changed

+183
-51
lines changed

tools/download.go

+183-51
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package tools
22

33
import (
44
"archive/tar"
5+
"archive/zip"
56
"bytes"
67
"compress/bzip2"
8+
"compress/gzip"
79
"crypto/sha256"
810
"encoding/hex"
911
"encoding/json"
@@ -13,10 +15,13 @@ import (
1315
"net/http"
1416
"os"
1517
"path"
18+
"path/filepath"
1619
"runtime"
20+
"strings"
1721

1822
"github.com/arduino/arduino-create-agent/utilities"
19-
"github.com/pivotal-golang/archiver/extractor"
23+
"github.com/blang/semver"
24+
"github.com/xrash/smetrics"
2025
)
2126

2227
type system struct {
@@ -48,6 +53,10 @@ var systems = map[string]string{
4853
"windows386": "i686-mingw32",
4954
}
5055

56+
func mimeType(data []byte) (string, error) {
57+
return http.DetectContentType(data[0:512]), nil
58+
}
59+
5160
// Download will parse the index at the indexURL for the tool to download.
5261
// It will extract it in a folder in .arduino-create, and it will update the
5362
// Installed map.
@@ -92,6 +101,8 @@ func (t *Tools) Download(name, version, behaviour string) error {
92101
var data index
93102
json.Unmarshal(body, &data)
94103

104+
t.Logger.Println(string(body))
105+
95106
// Find the tool by name
96107
correctTool := findTool(name, version, data)
97108

@@ -101,10 +112,13 @@ func (t *Tools) Download(name, version, behaviour string) error {
101112

102113
// Find the url based on system
103114
var correctSystem system
115+
max_similarity := 0.8
104116

105117
for _, s := range correctTool.Systems {
106-
if s.Host == systems[runtime.GOOS+runtime.GOARCH] {
118+
similarity := smetrics.Jaro(s.Host, systems[runtime.GOOS+runtime.GOARCH])
119+
if similarity > max_similarity {
107120
correctSystem = s
121+
max_similarity = similarity
108122
}
109123
}
110124

@@ -133,18 +147,26 @@ func (t *Tools) Download(name, version, behaviour string) error {
133147
// Decompress
134148
t.Logger.Println("Unpacking tool " + name)
135149

136-
location := path.Join(dir(), name, version)
150+
location := path.Join(dir(), correctTool.Name, correctTool.Version)
137151
err = os.RemoveAll(location)
138152

139153
if err != nil {
140154
return err
141155
}
142156

143-
switch path.Ext(correctSystem.URL) {
144-
case ".zip":
157+
srcType, err := mimeType(body)
158+
if err != nil {
159+
return err
160+
}
161+
162+
switch srcType {
163+
case "application/zip":
145164
location, err = extractZip(body, location)
146-
case ".bz2":
165+
case "application/x-bz2":
166+
case "application/octet-stream":
147167
location, err = extractBz2(body, location)
168+
case "application/x-gzip":
169+
location, err = extractTarGz(body, location)
148170
default:
149171
return errors.New("Unknown extension for file " + correctSystem.URL)
150172
}
@@ -155,20 +177,18 @@ func (t *Tools) Download(name, version, behaviour string) error {
155177

156178
// Ensure that the files are executable
157179
t.Logger.Println("Ensure that the files are executable")
158-
err = makeExecutable(location)
159-
if err != nil {
160-
return err
161-
}
162180

163181
// Update the tool map
164182
t.Logger.Println("Updating map with location " + location)
165183

166-
t.installed[key] = location
184+
t.installed[name] = location
185+
t.installed[name+"-"+correctTool.Version] = location
167186
return t.writeMap()
168187
}
169188

170189
func findTool(name, version string, data index) tool {
171190
var correctTool tool
191+
correctTool.Version = "0.0"
172192

173193
for _, p := range data.Packages {
174194
for _, t := range p.Tools {
@@ -178,7 +198,9 @@ func findTool(name, version string, data index) tool {
178198
}
179199
} else {
180200
// Find latest
181-
if t.Name == name && t.Version > correctTool.Version {
201+
v1, _ := semver.Make(t.Version)
202+
v2, _ := semver.Make(correctTool.Version)
203+
if t.Name == name && v1.Compare(v2) > 0 {
182204
correctTool = t
183205
}
184206
}
@@ -187,75 +209,185 @@ func findTool(name, version string, data index) tool {
187209
return correctTool
188210
}
189211

212+
func findBaseDir(dirList []string) string {
213+
baseDir := ""
214+
for index, _ := range dirList {
215+
candidateBaseDir := dirList[index]
216+
for i := index; i < len(dirList); i++ {
217+
if !strings.Contains(dirList[i], candidateBaseDir) {
218+
return baseDir
219+
}
220+
}
221+
baseDir = candidateBaseDir
222+
}
223+
return baseDir
224+
}
225+
190226
func extractZip(body []byte, location string) (string, error) {
191227
path, err := utilities.SaveFileonTempDir("tooldownloaded.zip", bytes.NewReader(body))
228+
r, err := zip.OpenReader(path)
192229
if err != nil {
193-
return "", err
230+
return location, err
194231
}
195232

196-
e := extractor.NewZip()
197-
err = e.Extract(path, location)
198-
if err != nil {
199-
return "", err
233+
var dirList []string
234+
235+
for _, f := range r.File {
236+
if f.FileInfo().IsDir() {
237+
dirList = append(dirList, f.Name)
238+
}
239+
}
240+
241+
basedir := findBaseDir(dirList)
242+
243+
for _, f := range r.File {
244+
fullname := filepath.Join(location, strings.Replace(f.Name, basedir, "", -1))
245+
if f.FileInfo().IsDir() {
246+
os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
247+
} else {
248+
os.MkdirAll(filepath.Dir(fullname), 0755)
249+
perms := f.FileInfo().Mode().Perm()
250+
out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
251+
if err != nil {
252+
return location, err
253+
}
254+
rc, err := f.Open()
255+
if err != nil {
256+
return location, err
257+
}
258+
_, err = io.CopyN(out, rc, f.FileInfo().Size())
259+
if err != nil {
260+
return location, err
261+
}
262+
rc.Close()
263+
out.Close()
264+
265+
mtime := f.FileInfo().ModTime()
266+
err = os.Chtimes(fullname, mtime, mtime)
267+
if err != nil {
268+
return location, err
269+
}
270+
}
200271
}
201-
return "", nil
272+
return location, nil
202273
}
203274

204-
func extractBz2(body []byte, location string) (string, error) {
205-
tarFile := bzip2.NewReader(bytes.NewReader(body))
275+
func extractTarGz(body []byte, location string) (string, error) {
276+
bodyCopy := make([]byte, len(body))
277+
copy(bodyCopy, body)
278+
tarFile, _ := gzip.NewReader(bytes.NewReader(body))
206279
tarReader := tar.NewReader(tarFile)
207280

208-
var subfolder string
281+
var dirList []string
209282

210-
i := 0
211283
for {
212284
header, err := tarReader.Next()
285+
if err == io.EOF {
286+
break
287+
}
288+
if header.FileInfo().IsDir() {
289+
dirList = append(dirList, header.Name)
290+
}
291+
}
292+
293+
basedir := findBaseDir(dirList)
294+
295+
tarFile, _ = gzip.NewReader(bytes.NewReader(bodyCopy))
296+
tarReader = tar.NewReader(tarFile)
213297

298+
for {
299+
header, err := tarReader.Next()
214300
if err == io.EOF {
215301
break
302+
} else if err != nil {
303+
//return location, err
304+
}
305+
306+
path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
307+
info := header.FileInfo()
308+
309+
if info.IsDir() {
310+
if err = os.MkdirAll(path, info.Mode()); err != nil {
311+
return location, err
312+
}
313+
continue
314+
}
315+
316+
if header.Typeflag == tar.TypeSymlink {
317+
err = os.Symlink(header.Linkname, path)
318+
continue
216319
}
217320

321+
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
322+
if err != nil {
323+
//return location, err
324+
}
325+
defer file.Close()
326+
_, err = io.Copy(file, tarReader)
218327
if err != nil {
219-
return "", err
328+
//return location, err
220329
}
330+
}
331+
return location, nil
332+
}
333+
334+
func extractBz2(body []byte, location string) (string, error) {
335+
bodyCopy := make([]byte, len(body))
336+
copy(bodyCopy, body)
337+
tarFile := bzip2.NewReader(bytes.NewReader(body))
338+
tarReader := tar.NewReader(tarFile)
221339

222-
filePath := path.Join(location, header.Name)
340+
var dirList []string
223341

224-
// We get the name of the subfolder
225-
if i == 0 {
226-
subfolder = filePath
342+
for {
343+
header, err := tarReader.Next()
344+
if err == io.EOF {
345+
break
227346
}
347+
if header.FileInfo().IsDir() {
348+
dirList = append(dirList, header.Name)
349+
}
350+
}
228351

229-
switch header.Typeflag {
230-
case tar.TypeDir:
231-
err = os.MkdirAll(filePath, os.FileMode(header.Mode))
232-
case tar.TypeReg:
233-
f, err := os.Create(filePath)
234-
if err != nil {
235-
break
236-
}
237-
defer f.Close()
238-
_, err = io.Copy(f, tarReader)
239-
case tar.TypeRegA:
240-
f, err := os.Create(filePath)
241-
if err != nil {
242-
break
352+
basedir := findBaseDir(dirList)
353+
354+
tarFile = bzip2.NewReader(bytes.NewReader(bodyCopy))
355+
tarReader = tar.NewReader(tarFile)
356+
357+
for {
358+
header, err := tarReader.Next()
359+
if err == io.EOF {
360+
break
361+
} else if err != nil {
362+
//return location, err
363+
}
364+
365+
path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
366+
info := header.FileInfo()
367+
368+
if info.IsDir() {
369+
if err = os.MkdirAll(path, info.Mode()); err != nil {
370+
return location, err
243371
}
244-
defer f.Close()
245-
_, err = io.Copy(f, tarReader)
246-
case tar.TypeSymlink:
247-
err = os.Symlink(header.Linkname, filePath)
248-
default:
249-
err = errors.New("Unknown header in tar.bz2 file")
372+
continue
250373
}
251374

252-
if err != nil {
253-
return "", err
375+
if header.Typeflag == tar.TypeSymlink {
376+
err = os.Symlink(header.Linkname, path)
377+
continue
254378
}
255379

256-
i++
380+
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
381+
if err != nil {
382+
//return location, err
383+
}
384+
defer file.Close()
385+
_, err = io.Copy(file, tarReader)
386+
if err != nil {
387+
//return location, err
388+
}
257389
}
258-
return subfolder, nil
390+
return location, nil
259391
}
260392

261393
func makeExecutable(location string) error {

0 commit comments

Comments
 (0)