Skip to content

Commit fa8874f

Browse files
authoredDec 18, 2023
Use codeclysm/extract and remove custom implementation (#867)
* replace test with a meaningful one The tools are the one coming with the cores installed in the builder * use `codeclysm/extract` and remove extract code * fix cleanup in tests * enlarge the scope of the test: verify dir tree is handled correctly * the archives are first extracted in tmp and then moved to correct place we remove the root dir if we find one. The logic is similar to: https://github.com/arduino/arduino-cli/blob/7a146635aaa740e748b84bf8fbfdccf1cc420c61/arduino/resources/install.go#L34 * fix test failing
1 parent 0fd774a commit fa8874f

File tree

4 files changed

+548
-423
lines changed

4 files changed

+548
-423
lines changed
 

‎tools/download.go

Lines changed: 49 additions & 288 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,8 @@
1616
package tools
1717

1818
import (
19-
"archive/tar"
20-
"archive/zip"
2119
"bytes"
22-
"compress/bzip2"
23-
"compress/gzip"
20+
"context"
2421
"crypto/sha256"
2522
"encoding/hex"
2623
"encoding/json"
@@ -30,14 +27,13 @@ import (
3027
"net/http"
3128
"os"
3229
"os/exec"
33-
"path"
3430
"path/filepath"
3531
"runtime"
36-
"strings"
3732

38-
"github.com/arduino/arduino-create-agent/utilities"
3933
"github.com/arduino/arduino-create-agent/v2/pkgs"
34+
"github.com/arduino/go-paths-helper"
4035
"github.com/blang/semver"
36+
"github.com/codeclysm/extract/v3"
4137
)
4238

4339
// public vars to allow override in the tests
@@ -46,10 +42,6 @@ var (
4642
Arch = runtime.GOARCH
4743
)
4844

49-
func mimeType(data []byte) (string, error) {
50-
return http.DetectContentType(data[0:512]), nil
51-
}
52-
5345
func pathExists(path string) bool {
5446
_, err := os.Stat(path)
5547
if err == nil {
@@ -129,39 +121,46 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
129121
return errors.New("checksum doesn't match")
130122
}
131123

132-
// Decompress
133-
t.logger("Unpacking tool " + name)
134-
135-
location := t.directory.Join(pack, correctTool.Name, correctTool.Version).String()
136-
err = os.RemoveAll(location)
137-
124+
tempPath := paths.TempDir()
125+
// Create a temporary dir to extract package
126+
if err := tempPath.MkdirAll(); err != nil {
127+
return fmt.Errorf("creating temp dir for extraction: %s", err)
128+
}
129+
tempDir, err := tempPath.MkTempDir("package-")
138130
if err != nil {
139-
return err
131+
return fmt.Errorf("creating temp dir for extraction: %s", err)
140132
}
133+
defer tempDir.RemoveAll()
141134

142-
srcType, err := mimeType(body)
135+
t.logger("Unpacking tool " + name)
136+
ctx := context.Background()
137+
reader := bytes.NewReader(body)
138+
// Extract into temp directory
139+
if err := extract.Archive(ctx, reader, tempDir.String(), nil); err != nil {
140+
return fmt.Errorf("extracting archive: %s", err)
141+
}
142+
143+
location := t.directory.Join(pack, correctTool.Name, correctTool.Version)
144+
err = location.RemoveAll()
143145
if err != nil {
144146
return err
145147
}
146148

147-
switch srcType {
148-
case "application/zip":
149-
location, err = extractZip(t.logger, body, location)
150-
case "application/x-bz2":
151-
case "application/octet-stream":
152-
location, err = extractBz2(t.logger, body, location)
153-
case "application/x-gzip":
154-
location, err = extractTarGz(t.logger, body, location)
155-
default:
156-
return errors.New("Unknown extension for file " + correctSystem.URL)
149+
// Check package content and find package root dir
150+
root, err := findPackageRoot(tempDir)
151+
if err != nil {
152+
return fmt.Errorf("searching package root dir: %s", err)
157153
}
158154

159-
if err != nil {
160-
t.logger("Error extracting the archive: " + err.Error())
161-
return err
155+
if err := root.Rename(location); err != nil {
156+
if err := root.CopyDirTo(location); err != nil {
157+
return fmt.Errorf("moving extracted archive to destination dir: %s", err)
158+
}
162159
}
163160

164-
err = t.installDrivers(location)
161+
// if the tool contains a post_install script, run it: it means it is a tool that needs to install drivers
162+
// AFAIK this is only the case for the windows-driver tool
163+
err = t.installDrivers(location.String())
165164
if err != nil {
166165
return err
167166
}
@@ -170,13 +169,27 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
170169
t.logger("Ensure that the files are executable")
171170

172171
// Update the tool map
173-
t.logger("Updating map with location " + location)
172+
t.logger("Updating map with location " + location.String())
174173

175-
t.setMapValue(name, location)
176-
t.setMapValue(name+"-"+correctTool.Version, location)
174+
t.setMapValue(name, location.String())
175+
t.setMapValue(name+"-"+correctTool.Version, location.String())
177176
return t.writeMap()
178177
}
179178

179+
func findPackageRoot(parent *paths.Path) (*paths.Path, error) {
180+
files, err := parent.ReadDir()
181+
if err != nil {
182+
return nil, fmt.Errorf("reading package root dir: %s", err)
183+
}
184+
files.FilterOutPrefix("__MACOSX")
185+
186+
// if there is only one dir, it is the root dir
187+
if len(files) == 1 && files[0].IsDir() {
188+
return files[0], nil
189+
}
190+
return parent, nil
191+
}
192+
180193
func findTool(pack, name, version string, data pkgs.Index) (pkgs.Tool, pkgs.System) {
181194
var correctTool pkgs.Tool
182195
correctTool.Version = "0.0"
@@ -207,258 +220,6 @@ func findTool(pack, name, version string, data pkgs.Index) (pkgs.Tool, pkgs.Syst
207220
return correctTool, correctSystem
208221
}
209222

210-
func commonPrefix(sep byte, paths []string) string {
211-
// Handle special cases.
212-
switch len(paths) {
213-
case 0:
214-
return ""
215-
case 1:
216-
return path.Clean(paths[0])
217-
}
218-
219-
c := []byte(path.Clean(paths[0]))
220-
221-
// We add a trailing sep to handle: common prefix directory is included in the path list
222-
// (e.g. /home/user1, /home/user1/foo, /home/user1/bar).
223-
// path.Clean will have cleaned off trailing / separators with
224-
// the exception of the root directory, "/" making it "//"
225-
// but this will get fixed up to "/" below).
226-
c = append(c, sep)
227-
228-
// Ignore the first path since it's already in c
229-
for _, v := range paths[1:] {
230-
// Clean up each path before testing it
231-
v = path.Clean(v) + string(sep)
232-
233-
// Find the first non-common byte and truncate c
234-
if len(v) < len(c) {
235-
c = c[:len(v)]
236-
}
237-
for i := 0; i < len(c); i++ {
238-
if v[i] != c[i] {
239-
c = c[:i]
240-
break
241-
}
242-
}
243-
}
244-
245-
// Remove trailing non-separator characters and the final separator
246-
for i := len(c) - 1; i >= 0; i-- {
247-
if c[i] == sep {
248-
c = c[:i]
249-
break
250-
}
251-
}
252-
253-
return string(c)
254-
}
255-
256-
func removeStringFromSlice(s []string, r string) []string {
257-
for i, v := range s {
258-
if v == r {
259-
return append(s[:i], s[i+1:]...)
260-
}
261-
}
262-
return s
263-
}
264-
265-
func findBaseDir(dirList []string) string {
266-
if len(dirList) == 1 {
267-
return path.Dir(dirList[0]) + "/"
268-
}
269-
270-
// https://github.com/backdrop-ops/contrib/issues/55#issuecomment-73814500
271-
dontdiff := []string{"pax_global_header"}
272-
for _, v := range dontdiff {
273-
dirList = removeStringFromSlice(dirList, v)
274-
}
275-
276-
commonBaseDir := commonPrefix('/', dirList)
277-
if commonBaseDir != "" {
278-
commonBaseDir = commonBaseDir + "/"
279-
}
280-
return commonBaseDir
281-
}
282-
283-
func extractZip(log func(msg string), body []byte, location string) (string, error) {
284-
path, _ := utilities.SaveFileonTempDir("tooldownloaded.zip", bytes.NewReader(body))
285-
r, err := zip.OpenReader(path)
286-
if err != nil {
287-
return location, err
288-
}
289-
290-
var dirList []string
291-
292-
for _, f := range r.File {
293-
dirList = append(dirList, f.Name)
294-
}
295-
296-
basedir := findBaseDir(dirList)
297-
log(fmt.Sprintf("selected baseDir %s from Zip Archive Content: %v", basedir, dirList))
298-
299-
for _, f := range r.File {
300-
fullname := filepath.Join(location, strings.Replace(f.Name, basedir, "", -1))
301-
log(fmt.Sprintf("generated fullname %s removing %s from %s", fullname, basedir, f.Name))
302-
if f.FileInfo().IsDir() {
303-
os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
304-
} else {
305-
os.MkdirAll(filepath.Dir(fullname), 0755)
306-
perms := f.FileInfo().Mode().Perm()
307-
out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
308-
if err != nil {
309-
return location, err
310-
}
311-
rc, err := f.Open()
312-
if err != nil {
313-
return location, err
314-
}
315-
_, err = io.CopyN(out, rc, f.FileInfo().Size())
316-
if err != nil {
317-
return location, err
318-
}
319-
rc.Close()
320-
out.Close()
321-
322-
mtime := f.FileInfo().ModTime()
323-
err = os.Chtimes(fullname, mtime, mtime)
324-
if err != nil {
325-
return location, err
326-
}
327-
}
328-
}
329-
return location, nil
330-
}
331-
332-
func extractTarGz(log func(msg string), body []byte, location string) (string, error) {
333-
bodyCopy := make([]byte, len(body))
334-
copy(bodyCopy, body)
335-
tarFile, _ := gzip.NewReader(bytes.NewReader(body))
336-
tarReader := tar.NewReader(tarFile)
337-
338-
var dirList []string
339-
340-
for {
341-
header, err := tarReader.Next()
342-
if err == io.EOF {
343-
break
344-
}
345-
dirList = append(dirList, header.Name)
346-
}
347-
348-
basedir := findBaseDir(dirList)
349-
log(fmt.Sprintf("selected baseDir %s from TarGz Archive Content: %v", basedir, dirList))
350-
351-
tarFile, _ = gzip.NewReader(bytes.NewReader(bodyCopy))
352-
tarReader = tar.NewReader(tarFile)
353-
354-
for {
355-
header, err := tarReader.Next()
356-
if err == io.EOF {
357-
break
358-
} else if err != nil {
359-
return location, err
360-
}
361-
362-
path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
363-
info := header.FileInfo()
364-
365-
// Create parent folder
366-
dirmode := info.Mode() | os.ModeDir | 0700
367-
if err = os.MkdirAll(filepath.Dir(path), dirmode); err != nil {
368-
return location, err
369-
}
370-
371-
if info.IsDir() {
372-
if err = os.MkdirAll(path, info.Mode()); err != nil {
373-
return location, err
374-
}
375-
continue
376-
}
377-
378-
if header.Typeflag == tar.TypeSymlink {
379-
_ = os.Symlink(header.Linkname, path)
380-
continue
381-
}
382-
383-
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
384-
if err != nil {
385-
continue
386-
}
387-
_, err = io.Copy(file, tarReader)
388-
if err != nil {
389-
return location, err
390-
}
391-
file.Close()
392-
}
393-
return location, nil
394-
}
395-
396-
func extractBz2(log func(msg string), body []byte, location string) (string, error) {
397-
bodyCopy := make([]byte, len(body))
398-
copy(bodyCopy, body)
399-
tarFile := bzip2.NewReader(bytes.NewReader(body))
400-
tarReader := tar.NewReader(tarFile)
401-
402-
var dirList []string
403-
404-
for {
405-
header, err := tarReader.Next()
406-
if err == io.EOF {
407-
break
408-
}
409-
dirList = append(dirList, header.Name)
410-
}
411-
412-
basedir := findBaseDir(dirList)
413-
log(fmt.Sprintf("selected baseDir %s from Bz2 Archive Content: %v", basedir, dirList))
414-
415-
tarFile = bzip2.NewReader(bytes.NewReader(bodyCopy))
416-
tarReader = tar.NewReader(tarFile)
417-
418-
for {
419-
header, err := tarReader.Next()
420-
if err == io.EOF {
421-
break
422-
} else if err != nil {
423-
continue
424-
//return location, err
425-
}
426-
427-
path := filepath.Join(location, strings.Replace(header.Name, basedir, "", -1))
428-
info := header.FileInfo()
429-
430-
// Create parent folder
431-
dirmode := info.Mode() | os.ModeDir | 0700
432-
if err = os.MkdirAll(filepath.Dir(path), dirmode); err != nil {
433-
return location, err
434-
}
435-
436-
if info.IsDir() {
437-
if err = os.MkdirAll(path, info.Mode()); err != nil {
438-
return location, err
439-
}
440-
continue
441-
}
442-
443-
if header.Typeflag == tar.TypeSymlink {
444-
_ = os.Symlink(header.Linkname, path)
445-
continue
446-
}
447-
448-
file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, info.Mode())
449-
if err != nil {
450-
continue
451-
//return location, err
452-
}
453-
_, err = io.Copy(file, tarReader)
454-
if err != nil {
455-
return location, err
456-
}
457-
file.Close()
458-
}
459-
return location, nil
460-
}
461-
462223
func (t *Tools) installDrivers(location string) error {
463224
OkPressed := 6
464225
extension := ".bat"

‎tools/download_test.go

Lines changed: 60 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@ package tools
1717

1818
import (
1919
"encoding/json"
20-
"fmt"
21-
"io"
22-
"net/http"
23-
"os"
24-
"path"
20+
"runtime"
2521
"testing"
22+
"time"
2623

24+
"github.com/arduino/arduino-create-agent/index"
2725
"github.com/arduino/arduino-create-agent/v2/pkgs"
2826
"github.com/arduino/go-paths-helper"
29-
"github.com/stretchr/testify/assert"
3027
"github.com/stretchr/testify/require"
3128
)
3229

@@ -44,6 +41,10 @@ func TestDownloadCorrectPlatform(t *testing.T) {
4441
{"windows", "amd64", "x86_64-mingw32"},
4542
{"linux", "arm", "arm-linux-gnueabihf"},
4643
}
44+
defer func() {
45+
OS = runtime.GOOS // restore `runtime.OS`
46+
Arch = runtime.GOARCH // restore `runtime.ARCH`
47+
}()
4748
testIndex := paths.New("testdata", "test_tool_index.json")
4849
buf, err := testIndex.ReadFile()
4950
require.NoError(t, err)
@@ -76,6 +77,10 @@ func TestDownloadFallbackPlatform(t *testing.T) {
7677
{"darwin", "arm64", "i386-apple-darwin11"},
7778
{"windows", "amd64", "i686-mingw32"},
7879
}
80+
defer func() {
81+
OS = runtime.GOOS // restore `runtime.OS`
82+
Arch = runtime.GOARCH // restore `runtime.ARCH`
83+
}()
7984
testIndex := paths.New("testdata", "test_tool_index.json")
8085
buf, err := testIndex.ReadFile()
8186
require.NoError(t, err)
@@ -98,99 +103,59 @@ func TestDownloadFallbackPlatform(t *testing.T) {
98103
}
99104
}
100105

101-
func Test_findBaseDir(t *testing.T) {
102-
cases := []struct {
103-
dirList []string
104-
want string
106+
func TestDownload(t *testing.T) {
107+
testCases := []struct {
108+
name string
109+
version string
110+
filesCreated []string
105111
}{
106-
{[]string{"bin/bossac"}, "bin/"},
107-
{[]string{"bin/", "bin/bossac"}, "bin/"},
108-
{[]string{"bin/", "bin/bossac", "example"}, ""},
109-
{[]string{"avrdude/bin/",
110-
"avrdude/bin/avrdude.exe",
111-
"avrdude/bin/remove_giveio.bat",
112-
"avrdude/bin/status_giveio.bat",
113-
"avrdude/bin/giveio.sys",
114-
"avrdude/bin/loaddrv.exe",
115-
"avrdude/bin/libusb0.dll",
116-
"avrdude/bin/install_giveio.bat",
117-
"avrdude/etc/avrdude.conf"}, "avrdude/"},
118-
{[]string{"pax_global_header", "bin/", "bin/bossac"}, "bin/"},
112+
{"avrdude", "6.3.0-arduino17", []string{"bin", "etc"}},
113+
{"bossac", "1.6.1-arduino", []string{"bossac"}},
114+
{"bossac", "1.7.0-arduino3", []string{"bossac"}},
115+
{"bossac", "1.9.1-arduino2", []string{"bossac"}},
116+
{"openocd", "0.11.0-arduino2", []string{"bin", "share"}},
117+
{"dfu-util", "0.10.0-arduino1", []string{"dfu-prefix", "dfu-suffix", "dfu-util"}},
118+
{"rp2040tools", "1.0.6", []string{"elf2uf2", "picotool", "pioasm", "rp2040load"}},
119+
{"esptool_py", "4.5.1", []string{"esptool"}},
120+
{"arduino-fwuploader", "2.2.2", []string{"arduino-fwuploader"}},
121+
{"fwupdater", "0.1.12", []string{"firmwares", "FirmwareUploader"}}, // old legacy tool
119122
}
120-
for _, tt := range cases {
121-
t.Run(fmt.Sprintln(tt.dirList), func(t *testing.T) {
122-
if got := findBaseDir(tt.dirList); got != tt.want {
123-
t.Errorf("findBaseDir() = got %v, want %v", got, tt.want)
124-
}
125-
})
123+
// prepare the test environment
124+
tempDir := t.TempDir()
125+
tempDirPath := paths.New(tempDir)
126+
testIndex := index.Resource{
127+
IndexFile: *paths.New("testdata", "test_tool_index.json"),
128+
LastRefresh: time.Now(),
126129
}
127-
}
128-
129-
func TestTools_DownloadAndUnpackBehaviour(t *testing.T) {
130-
urls := []string{
131-
"https://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-armhf-pc-linux-gnu.tar.bz2",
132-
"https://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-aarch64-pc-linux-gnu.tar.bz2",
133-
"https://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-i386-apple-darwin11.tar.bz2",
134-
"https://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-x86_64-pc-linux-gnu.tar.bz2",
135-
"https://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-i686-pc-linux-gnu.tar.bz2",
136-
"https://downloads.arduino.cc/tools/avrdude-6.3.0-arduino14-i686-w64-mingw32.zip",
137-
}
138-
expectedDirList := []string{"bin", "etc"}
139-
140-
tmpDir, err := os.MkdirTemp("", "download_test")
141-
if err != nil {
142-
t.Fatal(err)
143-
}
144-
defer os.RemoveAll(tmpDir)
145-
146-
for _, url := range urls {
147-
t.Log("Downloading tool from " + url)
148-
resp, err := http.Get(url)
149-
if err != nil {
150-
t.Errorf("%v", err)
151-
}
152-
defer resp.Body.Close()
130+
testTools := New(tempDirPath, &testIndex, func(msg string) { t.Log(msg) })
153131

154-
// Read the body
155-
body, err := io.ReadAll(resp.Body)
156-
if err != nil {
157-
t.Errorf("%v", err)
158-
}
159-
160-
location := path.Join(tmpDir, "username", "arduino", "avrdude", "6.3.0-arduino14")
161-
os.MkdirAll(location, os.ModePerm)
162-
err = os.RemoveAll(location)
163-
164-
if err != nil {
165-
t.Errorf("%v", err)
166-
}
167-
168-
srcType, err := mimeType(body)
169-
if err != nil {
170-
t.Errorf("%v", err)
171-
}
172-
173-
switch srcType {
174-
case "application/zip":
175-
location, err = extractZip(func(msg string) { t.Log(msg) }, body, location)
176-
case "application/x-bz2":
177-
case "application/octet-stream":
178-
location, err = extractBz2(func(msg string) { t.Log(msg) }, body, location)
179-
case "application/x-gzip":
180-
location, err = extractTarGz(func(msg string) { t.Log(msg) }, body, location)
181-
default:
182-
t.Errorf("no suitable type found")
183-
}
184-
files, err := os.ReadDir(location)
185-
if err != nil {
186-
t.Errorf("%v", err)
187-
}
188-
dirList := []string{}
189-
for _, f := range files {
190-
dirList = append(dirList, f.Name())
191-
}
132+
for _, tc := range testCases {
133+
t.Run(tc.name+"-"+tc.version, func(t *testing.T) {
134+
// Download the tool
135+
err := testTools.Download("arduino-test", tc.name, tc.version, "replace")
136+
require.NoError(t, err)
137+
138+
// Check that the tool has been downloaded
139+
toolDir := tempDirPath.Join("arduino-test", tc.name, tc.version)
140+
require.DirExists(t, toolDir.String())
141+
142+
// Check that the files have been created
143+
for _, file := range tc.filesCreated {
144+
filePath := toolDir.Join(file)
145+
if filePath.IsDir() {
146+
require.DirExists(t, filePath.String())
147+
} else {
148+
if OS == "windows" {
149+
require.FileExists(t, filePath.String()+".exe")
150+
} else {
151+
require.FileExists(t, filePath.String())
152+
}
153+
}
154+
}
192155

193-
assert.ElementsMatchf(t, dirList, expectedDirList, "error message %s", "formatted")
156+
// Check that the tool has been installed
157+
_, ok := testTools.getMapValue(tc.name + "-" + tc.version)
158+
require.True(t, ok)
159+
})
194160
}
195-
196161
}

‎tools/testdata/test_tool_index.json

Lines changed: 439 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,390 @@
4949
}
5050
],
5151
"tools": [
52+
{
53+
"name": "avrdude",
54+
"version": "6.3.0-arduino17",
55+
"systems": [
56+
{
57+
"size": "219631",
58+
"checksum": "SHA-256:2a8e68c5d803aa6f902ef219f177ec3a4c28275d85cbe272962ad2cd374f50d1",
59+
"host": "arm-linux-gnueabihf",
60+
"archiveFileName": "avrdude-6.3.0-arduino17-armhf-pc-linux-gnu.tar.bz2",
61+
"url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-armhf-pc-linux-gnu.tar.bz2"
62+
},
63+
{
64+
"size": "229852",
65+
"checksum": "SHA-256:6cf948f751acfe7b96684537f2291c766ec8b54b4f7dc95539864821456fa9fc",
66+
"host": "aarch64-linux-gnu",
67+
"archiveFileName": "avrdude-6.3.0-arduino17-aarch64-pc-linux-gnu.tar.bz2",
68+
"url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-aarch64-pc-linux-gnu.tar.bz2"
69+
},
70+
{
71+
"size": "279045",
72+
"checksum": "SHA-256:120cc9edaae699e7e9ac50b1b8eb0e7d51fdfa555bac54233c2511e6ee5418c9",
73+
"host": "x86_64-apple-darwin12",
74+
"archiveFileName": "avrdude-6.3.0-arduino17-x86_64-apple-darwin12.tar.bz2",
75+
"url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-x86_64-apple-darwin12.tar.bz2"
76+
},
77+
{
78+
"size": "254271",
79+
"checksum": "SHA-256:accdfb920af2aabf4f7461d2ac73c0751760f525216dc4e7657427a78c60d13d",
80+
"host": "x86_64-linux-gnu",
81+
"archiveFileName": "avrdude-6.3.0-arduino17-x86_64-pc-linux-gnu.tar.bz2",
82+
"url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-x86_64-pc-linux-gnu.tar.bz2"
83+
},
84+
{
85+
"size": "244550",
86+
"checksum": "SHA-256:5c8cc6c17db9300e1451fe41cd7178b0442b4490ee6fdbc0aed9811aef96c05f",
87+
"host": "i686-linux-gnu",
88+
"archiveFileName": "avrdude-6.3.0-arduino17-i686-pc-linux-gnu.tar.bz2",
89+
"url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-i686-pc-linux-gnu.tar.bz2"
90+
},
91+
{
92+
"size": "328460",
93+
"checksum": "SHA-256:e99188873c7c5ad8f8f906f068c33600e758b2e36cce3adbd518a21bd266749d",
94+
"host": "i686-mingw32",
95+
"archiveFileName": "avrdude-6.3.0-arduino17-i686-w64-mingw32.zip",
96+
"url": "http://downloads.arduino.cc/tools/avrdude-6.3.0-arduino17-i686-w64-mingw32.zip"
97+
}
98+
]
99+
},
100+
{
101+
"name": "bossac",
102+
"version": "1.6.1-arduino",
103+
"systems": [
104+
{
105+
"host": "arm-linux-gnueabihf",
106+
"url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-arm-linux-gnueabihf.tar.bz2",
107+
"archiveFileName": "bossac-1.6.1-arduino-arm-linux-gnueabihf.tar.bz2",
108+
"checksum": "SHA-256:8c4e63db982178919c824e7a35580dffc95c3426afa7285de3eb583982d4d391",
109+
"size": "201341"
110+
},
111+
{
112+
"host": "i686-mingw32",
113+
"url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-mingw32.tar.gz",
114+
"archiveFileName": "bossac-1.6.1-arduino-mingw32.tar.gz",
115+
"checksum": "SHA-256:d59f43e2e83a337d04c4ae88b195a4ee175b8d87fff4c43144d23412a4a9513b",
116+
"size": "222918"
117+
},
118+
{
119+
"host": "x86_64-apple-darwin",
120+
"url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-i386-apple-darwin14.5.0.tar.gz",
121+
"archiveFileName": "bossac-1.6.1-arduino-i386-apple-darwin14.5.0.tar.gz",
122+
"checksum": "SHA-256:2f80ef569a3fb19da60ab3489e49d8fe7d4699876acf30ff4938c632230a09aa",
123+
"size": "64587"
124+
},
125+
{
126+
"host": "x86_64-pc-linux-gnu",
127+
"url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-x86_64-linux-gnu.tar.gz",
128+
"archiveFileName": "bossac-1.6.1-arduino-x86_64-linux-gnu.tar.gz",
129+
"checksum": "SHA-256:b78afc66c00ccfdd69a08bd3959c260a0c64ccce78a71d5a1135ae4437ff40db",
130+
"size": "30869"
131+
},
132+
{
133+
"host": "i686-pc-linux-gnu",
134+
"url": "http://downloads.arduino.cc/bossac-1.6.1-arduino-i486-linux-gnu.tar.gz",
135+
"archiveFileName": "bossac-1.6.1-arduino-i486-linux-gnu.tar.gz",
136+
"checksum": "SHA-256:1e211347569d75193b337296a10dd25b0ce04419e3d7dc644355178b6b514f92",
137+
"size": "30320"
138+
}
139+
]
140+
},
141+
{
142+
"name": "bossac",
143+
"version": "1.7.0-arduino3",
144+
"systems": [
145+
{
146+
"host": "i686-mingw32",
147+
"url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-windows.tar.gz",
148+
"archiveFileName": "bossac-1.7.0-arduino3-windows.tar.gz",
149+
"checksum": "SHA-256:62745cc5a98c26949ec9041ef20420643c561ec43e99dae659debf44e6836526",
150+
"size": "3607421"
151+
},
152+
{
153+
"host": "x86_64-apple-darwin",
154+
"url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-osx.tar.gz",
155+
"archiveFileName": "bossac-1.7.0-arduino3-osx.tar.gz",
156+
"checksum": "SHA-256:adb3c14debd397d8135e9e970215c6972f0e592c7af7532fa15f9ce5e64b991f",
157+
"size": "75510"
158+
},
159+
{
160+
"host": "x86_64-pc-linux-gnu",
161+
"url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linux64.tar.gz",
162+
"archiveFileName": "bossac-1.7.0-arduino3-linux64.tar.gz",
163+
"checksum": "SHA-256:1ae54999c1f97234a5c603eb99ad39313b11746a4ca517269a9285afa05f9100",
164+
"size": "207271"
165+
},
166+
{
167+
"host": "i686-pc-linux-gnu",
168+
"url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linux32.tar.gz",
169+
"archiveFileName": "bossac-1.7.0-arduino3-linux32.tar.gz",
170+
"checksum": "SHA-256:4ac4354746d1a09258f49a43ef4d1baf030d81c022f8434774268b00f55d3ec3",
171+
"size": "193577"
172+
},
173+
{
174+
"host": "arm-linux-gnueabihf",
175+
"url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linuxarm.tar.gz",
176+
"archiveFileName": "bossac-1.7.0-arduino3-linuxarm.tar.gz",
177+
"checksum": "SHA-256:626c6cc548046901143037b782bf019af1663bae0d78cf19181a876fb9abbb90",
178+
"size": "193941"
179+
},
180+
{
181+
"host": "aarch64-linux-gnu",
182+
"url": "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linuxaarch64.tar.gz",
183+
"archiveFileName": "bossac-1.7.0-arduino3-linuxaarch64.tar.gz",
184+
"checksum": "SHA-256:a098b2cc23e29f0dc468416210d097c4a808752cd5da1a7b9b8b7b931a04180b",
185+
"size": "268365"
186+
}
187+
]
188+
},
189+
{
190+
"name": "bossac",
191+
"version": "1.9.1-arduino2",
192+
"systems": [
193+
{
194+
"host": "i686-mingw32",
195+
"url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-windows.tar.gz",
196+
"archiveFileName": "bossac-1.9.1-arduino2-windows.tar.gz",
197+
"checksum": "SHA-256:5c994d04354f0db8e4bea136f49866d2ba537f0af74b2e78026f2d4fc75e3e39",
198+
"size": "1260628"
199+
},
200+
{
201+
"host": "x86_64-apple-darwin",
202+
"url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-osx.tar.gz",
203+
"archiveFileName": "bossac-1.9.1-arduino2-osx.tar.gz",
204+
"checksum": "SHA-256:b7732129364a378676604db6579c9b8dab50dd965fb50d7a3afff1839c97ff80",
205+
"size": "47870"
206+
},
207+
{
208+
"host": "x86_64-pc-linux-gnu",
209+
"url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linux64.tar.gz",
210+
"archiveFileName": "bossac-1.9.1-arduino2-linux64.tar.gz",
211+
"checksum": "SHA-256:9eb549874391521999cee13dc823a2cfc8866b8246945339a281808d99c72d2c",
212+
"size": "399532"
213+
},
214+
{
215+
"host": "i686-pc-linux-gnu",
216+
"url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linux32.tar.gz",
217+
"archiveFileName": "bossac-1.9.1-arduino2-linux32.tar.gz",
218+
"checksum": "SHA-256:10d69f53f169f25afee2dd583dfd9dc803c10543e6c5260d106725cb0d174900",
219+
"size": "384951"
220+
},
221+
{
222+
"host": "arm-linux-gnueabihf",
223+
"url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linuxarm.tar.gz",
224+
"archiveFileName": "bossac-1.9.1-arduino2-linuxarm.tar.gz",
225+
"checksum": "SHA-256:c9539d161d23231b5beb1d09a71829744216c7f5bc2857a491999c3e567f5b19",
226+
"size": "361915"
227+
},
228+
{
229+
"host": "aarch64-linux-gnu",
230+
"url": "http://downloads.arduino.cc/tools/bossac-1.9.1-arduino2-linuxaarch64.tar.gz",
231+
"archiveFileName": "bossac-1.9.1-arduino2-linuxaarch64.tar.gz",
232+
"checksum": "SHA-256:c167fa0ea223966f4d21f5592da3888bcbfbae385be6c5c4e41f8abff35f5cb1",
233+
"size": "442853"
234+
}
235+
]
236+
},
237+
{
238+
"name": "openocd",
239+
"version": "0.11.0-arduino2",
240+
"systems": [
241+
{
242+
"size": "1902818",
243+
"checksum": "SHA-256:a1aa7f1435a61eafb72ee90722f2496d6a34a7a0f085d0315c2613e4a548b824",
244+
"host": "aarch64-linux-gnu",
245+
"archiveFileName": "openocd-0.11.0-arduino2-static-aarch64-linux-gnu.tar.bz2",
246+
"url": "http://downloads.arduino.cc/tools/openocd-0.11.0-arduino2-static-aarch64-linux-gnu.tar.bz2"
247+
},
248+
{
249+
"size": "1986716",
250+
"checksum": "SHA-256:57041130160be086e69387cceb4616eefc9819a0ef75de1f7c11aea57fb92699",
251+
"host": "arm-linux-gnueabihf",
252+
"archiveFileName": "openocd-0.11.0-arduino2-static-arm-linux-gnueabihf.tar.bz2",
253+
"url": "http://downloads.arduino.cc/tools/openocd-0.11.0-arduino2-static-arm-linux-gnueabihf.tar.bz2"
254+
},
255+
{
256+
"size": "1971364",
257+
"checksum": "SHA-256:6f4a8b77c8076aa18afb8438472526dff8c0d161a3ca68d0326163b59fcab663",
258+
"host": "i686-linux-gnu",
259+
"archiveFileName": "openocd-0.11.0-arduino2-static-i686-ubuntu12.04-linux-gnu.tar.bz2",
260+
"url": "http://downloads.arduino.cc/tools/openocd-0.11.0-arduino2-static-i686-ubuntu12.04-linux-gnu.tar.bz2"
261+
},
262+
{
263+
"size": "2460087",
264+
"checksum": "SHA-256:631010980f12b1e750c4c67ce012b31c5953caabf4d30607d806e3d2b717d4b8",
265+
"host": "i686-mingw32",
266+
"archiveFileName": "openocd-0.11.0-arduino2-static-i686-w64-mingw32.zip",
267+
"url": "http://downloads.arduino.cc/tools/openocd-0.11.0-arduino2-static-i686-w64-mingw32.zip"
268+
},
269+
{
270+
"size": "1893150",
271+
"checksum": "SHA-256:280e7234eba84e830e92d791ebc685286f71d2bc1d3347f93605ef170d54fef4",
272+
"host": "i386-apple-darwin11",
273+
"archiveFileName": "openocd-0.11.0-arduino2-static-x86_64-apple-darwin13.tar.bz2",
274+
"url": "http://downloads.arduino.cc/tools/openocd-0.11.0-arduino2-static-x86_64-apple-darwin13.tar.bz2"
275+
},
276+
{
277+
"size": "2052080",
278+
"checksum": "SHA-256:4d19b6e3906de1434ec86841e0e3138235714c655d45f037c0fabfa5e5c0681b",
279+
"host": "x86_64-linux-gnu",
280+
"archiveFileName": "openocd-0.11.0-arduino2-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2",
281+
"url": "http://downloads.arduino.cc/tools/openocd-0.11.0-arduino2-static-x86_64-ubuntu12.04-linux-gnu.tar.bz2"
282+
}
283+
]
284+
},
285+
{
286+
"name": "dfu-util",
287+
"version": "0.10.0-arduino1",
288+
"systems": [
289+
{
290+
"host": "i386-apple-darwin11",
291+
"url": "http://downloads.arduino.cc/tools/dfu-util-0.10.0-arduino1-osx.tar.bz2",
292+
"archiveFileName": "dfu-util-0.10.0-arduino1-osx.tar.bz2",
293+
"size": "73921",
294+
"checksum": "SHA-256:7562d128036759605828d64b8d672d42445a8d95555c4b9ba339f73a1711a640"
295+
},
296+
{
297+
"host": "arm-linux-gnueabihf",
298+
"url": "http://downloads.arduino.cc/tools/dfu-util-0.10.0-arduino1-arm.tar.bz2",
299+
"archiveFileName": "dfu-util-0.10.0-arduino1-arm.tar.bz2",
300+
"size": "272153",
301+
"checksum": "SHA-256:f1e550f40c235356b7fde1c59447bfbab28f768915d3c14bd858fe0576bfc5a9"
302+
},
303+
{
304+
"host": "aarch64-linux-gnu",
305+
"url": "http://downloads.arduino.cc/tools/dfu-util-0.10.0-arduino1-arm64.tar.bz2",
306+
"archiveFileName": "dfu-util-0.10.0-arduino1-arm64.tar.bz2",
307+
"size": "277886",
308+
"checksum": "SHA-256:ebfbd21d3030c500da1f83b9aae5b8c597bee04c3bde1ce0a51b41abeafc9614"
309+
},
310+
{
311+
"host": "x86_64-linux-gnu",
312+
"url": "http://downloads.arduino.cc/tools/dfu-util-0.10.0-arduino1-linux64.tar.bz2",
313+
"archiveFileName": "dfu-util-0.10.0-arduino1-linux64.tar.bz2",
314+
"size": "77184",
315+
"checksum": "SHA-256:13ef2ec591c1e8b0b7eb0a05da972ecd6695016e7a9607e332c7553899af9b4a"
316+
},
317+
{
318+
"host": "i686-linux-gnu",
319+
"url": "http://downloads.arduino.cc/tools/dfu-util-0.10.0-arduino1-linux32.tar.bz2",
320+
"archiveFileName": "dfu-util-0.10.0-arduino1-linux32.tar.bz2",
321+
"size": "81826",
322+
"checksum": "SHA-256:43599ec60c000e9ef016970a496d6ab2cbbe5a8b7df9d06ef3114ecf83f9d123"
323+
},
324+
{
325+
"host": "i686-mingw32",
326+
"url": "http://downloads.arduino.cc/tools/dfu-util-0.10.0-arduino1-windows.tar.bz2",
327+
"archiveFileName": "dfu-util-0.10.0-arduino1-windows.tar.bz2",
328+
"size": "464314",
329+
"checksum": "SHA-256:90816b669273ae796d734a2459c46bb340d4790783fd7aa01eb40c0443f1a9b1"
330+
}
331+
]
332+
},
333+
{
334+
"name": "rp2040tools",
335+
"version": "1.0.6",
336+
"systems": [
337+
{
338+
"host": "i386-apple-darwin11",
339+
"url": "http://downloads.arduino.cc/tools/rp2040tools-1.0.6-darwin_amd64.tar.bz2",
340+
"archiveFileName": "rp2040tools-1.0.6-darwin_amd64.tar.bz2",
341+
"size": "1717967",
342+
"checksum": "SHA-256:4e32aa4b8f36db40a17bfbdfd34d80da91710e30c3887732bf0c0bf0b02840a7"
343+
},
344+
{
345+
"host": "arm-linux-gnueabihf",
346+
"url": "http://downloads.arduino.cc/tools/rp2040tools-1.0.6-linux_arm.tar.bz2",
347+
"archiveFileName": "rp2040tools-1.0.6-linux_arm.tar.bz2",
348+
"size": "8702508",
349+
"checksum": "SHA-256:084a29accf0014bc79723fbb40057b95299c7ae63876f74494a077c987014cc3"
350+
},
351+
{
352+
"host": "aarch64-linux-gnu",
353+
"url": "http://downloads.arduino.cc/tools/rp2040tools-1.0.6-linux_arm64.tar.bz2",
354+
"archiveFileName": "rp2040tools-1.0.6-linux_arm64.tar.bz2",
355+
"size": "9037783",
356+
"checksum": "SHA-256:1a2a6cb1abf1f7b8198d494c8d8e838700297d748877be8232e02aaa5ca8d0df"
357+
},
358+
{
359+
"host": "x86_64-linux-gnu",
360+
"url": "http://downloads.arduino.cc/tools/rp2040tools-1.0.6-linux_amd64.tar.bz2",
361+
"archiveFileName": "rp2040tools-1.0.6-linux_amd64.tar.bz2",
362+
"size": "6108121",
363+
"checksum": "SHA-256:6e2ea818db1ff57f2d8e1e3010fbc5bdb5f28ff44f5a68900cae41d7d709f738"
364+
},
365+
{
366+
"host": "i686-linux-gnu",
367+
"url": "http://downloads.arduino.cc/tools/rp2040tools-1.0.6-linux_386.tar.bz2",
368+
"archiveFileName": "rp2040tools-1.0.6-linux_386.tar.bz2",
369+
"size": "6604083",
370+
"checksum": "SHA-256:ef339e2e0f5c7d5464b9911b612c634767daba39a6be977a1ffa41c95b9827a1"
371+
},
372+
{
373+
"host": "i686-mingw32",
374+
"url": "http://downloads.arduino.cc/tools/rp2040tools-1.0.6-windows_386.tar.bz2",
375+
"archiveFileName": "rp2040tools-1.0.6-windows_386.tar.bz2",
376+
"size": "3145329",
377+
"checksum": "SHA-256:26a5daebba68c2348dade33716a6e379ded89895ef0e49df1332964a724f6170"
378+
}
379+
]
380+
},
381+
{
382+
"name": "esptool_py",
383+
"version": "4.5.1",
384+
"systems": [
385+
{
386+
"host": "x86_64-pc-linux-gnu",
387+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-src.tar.gz",
388+
"archiveFileName": "esptool-v4.5.1-src.tar.gz",
389+
"checksum": "SHA-256:aa06831a7d88d8ccde4ea21241e983a08dbdae967290e181658b0d18bffc8f86",
390+
"size": "96922"
391+
},
392+
{
393+
"host": "i686-pc-linux-gnu",
394+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-src.tar.gz",
395+
"archiveFileName": "esptool-v4.5.1-src.tar.gz",
396+
"checksum": "SHA-256:aa06831a7d88d8ccde4ea21241e983a08dbdae967290e181658b0d18bffc8f86",
397+
"size": "96922"
398+
},
399+
{
400+
"host": "aarch64-linux-gnu",
401+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-src.tar.gz",
402+
"archiveFileName": "esptool-v4.5.1-src.tar.gz",
403+
"checksum": "SHA-256:aa06831a7d88d8ccde4ea21241e983a08dbdae967290e181658b0d18bffc8f86",
404+
"size": "96922"
405+
},
406+
{
407+
"host": "arm-linux-gnueabihf",
408+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-src.tar.gz",
409+
"archiveFileName": "esptool-v4.5.1-src.tar.gz",
410+
"checksum": "SHA-256:aa06831a7d88d8ccde4ea21241e983a08dbdae967290e181658b0d18bffc8f86",
411+
"size": "96922"
412+
},
413+
{
414+
"host": "x86_64-apple-darwin",
415+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-macos.tar.gz",
416+
"archiveFileName": "esptool-v4.5.1-macos.tar.gz",
417+
"checksum": "SHA-256:78b52acfd51541ceb97cee893b7d4d49b8ddc284602be8c73ea47e3d849e0956",
418+
"size": "5850888"
419+
},
420+
{
421+
"host": "x86_64-mingw32",
422+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-win64.zip",
423+
"archiveFileName": "esptool-v4.5.1-win64.zip",
424+
"checksum": "SHA-256:64d0c24499d46b80d6bd7a05c98bdacc3455ab6d503cc2a99e35711310216045",
425+
"size": "6638448"
426+
},
427+
{
428+
"host": "i686-mingw32",
429+
"url": "https://github.com/espressif/arduino-esp32/releases/download/2.0.7/esptool-v4.5.1-win64.zip",
430+
"archiveFileName": "esptool-v4.5.1-win64.zip",
431+
"checksum": "SHA-256:64d0c24499d46b80d6bd7a05c98bdacc3455ab6d503cc2a99e35711310216045",
432+
"size": "6638448"
433+
}
434+
]
435+
},
52436
{
53437
"name": "arduino-fwuploader",
54438
"version": "2.2.0",
@@ -130,6 +514,61 @@
130514
"size": "6829396"
131515
}
132516
]
517+
},
518+
{
519+
"name": "fwupdater",
520+
"version": "0.1.12",
521+
"systems": [
522+
{
523+
"host": "i686-linux-gnu",
524+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_Linux_32bit.tar.bz2",
525+
"archiveFileName": "FirmwareUploader_0.1.12_Linux_32bit.tar.bz2",
526+
"checksum": "SHA-256:2fec2bdfd20ad4950bc9ba37108dc2a7c152f569174279c0697efe1f5a0db781",
527+
"size": "26097546"
528+
},
529+
{
530+
"host": "x86_64-pc-linux-gnu",
531+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_Linux_64bit.tar.bz2",
532+
"archiveFileName": "FirmwareUploader_0.1.12_Linux_64bit.tar.bz2",
533+
"checksum": "SHA-256:ce57d0afef30cb7d3513f5da326346c99d6bf4923bbc2200634086811f3fb31e",
534+
"size": "26073327"
535+
},
536+
{
537+
"host": "i686-mingw32",
538+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_Windows_32bit.zip",
539+
"archiveFileName": "FirmwareUploader_0.1.12_Windows_32bit.zip",
540+
"checksum": "SHA-256:558568b453caa1c821def8cc6d34555d0c910eb7e7e871de3ae1c39ae6f01bdd",
541+
"size": "25743641"
542+
},
543+
{
544+
"host": "x86_64-mingw32",
545+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_Windows_64bit.zip",
546+
"archiveFileName": "FirmwareUploader_0.1.12_Windows_64bit.zip",
547+
"checksum": "SHA-256:ec16de33620985434280c92c3c322257b89bb67adf8fd4d5dd5f9467ea1e9e40",
548+
"size": "25851428"
549+
},
550+
{
551+
"host": "i386-apple-darwin11",
552+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_macOS_64bit.tar.bz2",
553+
"archiveFileName": "FirmwareUploader_0.1.12_macOS_64bit.tar.bz2",
554+
"checksum": "SHA-256:a470361b57f86ddfcaecd274d844af51ee1d23a71cd6c26e30fcef2152d1a03f",
555+
"size": "25792860"
556+
},
557+
{
558+
"host": "arm-linux-gnueabihf",
559+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_Linux_ARM.tar.bz2",
560+
"archiveFileName": "FirmwareUploader_0.1.12_Linux_ARM.tar.bz2",
561+
"checksum": "SHA-256:855fa0a9b942c3ee18906efc510bdfe30bf3334ff28ffbb476e648ff30033847",
562+
"size": "25936245"
563+
},
564+
{
565+
"host": "aarch64-linux-gnu",
566+
"url": "http://downloads.arduino.cc/tools/FirmwareUploader_0.1.12_Linux_ARM64.tar.bz2",
567+
"archiveFileName": "FirmwareUploader_0.1.12_Linux_ARM64.tar.bz2",
568+
"checksum": "SHA-256:691461e64fe075e9a79801347c2bd895fb72f8f2c45a7cd49056c6ad9efe8fc4",
569+
"size": "25967430"
570+
}
571+
]
133572
}
134573
]
135574
}

‎utilities/utilities.go

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package utilities
1717

1818
import (
19-
"archive/zip"
2019
"bytes"
2120
"crypto"
2221
"crypto/rsa"
@@ -29,7 +28,6 @@ import (
2928
"io"
3029
"os"
3130
"os/exec"
32-
"path"
3331
"path/filepath"
3432
"strings"
3533

@@ -120,44 +118,6 @@ func call(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) {
120118
return stack[0].Wait()
121119
}
122120

123-
// Unzip will unzip a file to a destination
124-
func Unzip(zippath string, destination string) (err error) {
125-
r, err := zip.OpenReader(zippath)
126-
if err != nil {
127-
return err
128-
}
129-
for _, f := range r.File {
130-
fullname := path.Join(destination, f.Name)
131-
if f.FileInfo().IsDir() {
132-
os.MkdirAll(fullname, f.FileInfo().Mode().Perm())
133-
} else {
134-
os.MkdirAll(filepath.Dir(fullname), 0755)
135-
perms := f.FileInfo().Mode().Perm()
136-
out, err := os.OpenFile(fullname, os.O_CREATE|os.O_RDWR, perms)
137-
if err != nil {
138-
return err
139-
}
140-
rc, err := f.Open()
141-
if err != nil {
142-
return err
143-
}
144-
_, err = io.CopyN(out, rc, f.FileInfo().Size())
145-
if err != nil {
146-
return err
147-
}
148-
rc.Close()
149-
out.Close()
150-
151-
mtime := f.FileInfo().ModTime()
152-
err = os.Chtimes(fullname, mtime, mtime)
153-
if err != nil {
154-
return err
155-
}
156-
}
157-
}
158-
return
159-
}
160-
161121
// SafeJoin performs a filepath.Join of 'parent' and 'subdir' but returns an error
162122
// if the resulting path points outside of 'parent'.
163123
func SafeJoin(parent, subdir string) (string, error) {

0 commit comments

Comments
 (0)
Please sign in to comment.