16
16
package tools
17
17
18
18
import (
19
- "archive/tar"
20
- "archive/zip"
21
19
"bytes"
22
- "compress/bzip2"
23
- "compress/gzip"
20
+ "context"
24
21
"crypto/sha256"
25
22
"encoding/hex"
26
23
"encoding/json"
@@ -30,14 +27,13 @@ import (
30
27
"net/http"
31
28
"os"
32
29
"os/exec"
33
- "path"
34
30
"path/filepath"
35
31
"runtime"
36
- "strings"
37
32
38
- "github.com/arduino/arduino-create-agent/utilities"
39
33
"github.com/arduino/arduino-create-agent/v2/pkgs"
34
+ "github.com/arduino/go-paths-helper"
40
35
"github.com/blang/semver"
36
+ "github.com/codeclysm/extract/v3"
41
37
)
42
38
43
39
// public vars to allow override in the tests
46
42
Arch = runtime .GOARCH
47
43
)
48
44
49
- func mimeType (data []byte ) (string , error ) {
50
- return http .DetectContentType (data [0 :512 ]), nil
51
- }
52
-
53
45
func pathExists (path string ) bool {
54
46
_ , err := os .Stat (path )
55
47
if err == nil {
@@ -129,39 +121,46 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
129
121
return errors .New ("checksum doesn't match" )
130
122
}
131
123
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-" )
138
130
if err != nil {
139
- return err
131
+ return fmt . Errorf ( "creating temp dir for extraction: %s" , err )
140
132
}
133
+ defer tempDir .RemoveAll ()
141
134
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 ()
143
145
if err != nil {
144
146
return err
145
147
}
146
148
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 )
157
153
}
158
154
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
+ }
162
159
}
163
160
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 ())
165
164
if err != nil {
166
165
return err
167
166
}
@@ -170,13 +169,27 @@ func (t *Tools) Download(pack, name, version, behaviour string) error {
170
169
t .logger ("Ensure that the files are executable" )
171
170
172
171
// Update the tool map
173
- t .logger ("Updating map with location " + location )
172
+ t .logger ("Updating map with location " + location . String () )
174
173
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 () )
177
176
return t .writeMap ()
178
177
}
179
178
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
+
180
193
func findTool (pack , name , version string , data pkgs.Index ) (pkgs.Tool , pkgs.System ) {
181
194
var correctTool pkgs.Tool
182
195
correctTool .Version = "0.0"
@@ -207,258 +220,6 @@ func findTool(pack, name, version string, data pkgs.Index) (pkgs.Tool, pkgs.Syst
207
220
return correctTool , correctSystem
208
221
}
209
222
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
-
462
223
func (t * Tools ) installDrivers (location string ) error {
463
224
OkPressed := 6
464
225
extension := ".bat"
0 commit comments