forked from arduino/arduino-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstall.go
138 lines (123 loc) · 4.15 KB
/
install.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
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].
package resources
import (
"context"
"fmt"
"os"
paths "github.com/arduino/go-paths-helper"
"github.com/codeclysm/extract/v3"
"go.bug.st/cleanup"
)
// Install installs the resource in three steps:
// - the archive is unpacked in a temporary subdir of tempPath
// - there should be only one root dir in the unpacked content
// - the only root dir is moved/renamed to/as the destination directory
// Note that tempPath and destDir must be on the same filesystem partition
// otherwise the last step will fail.
func (release *DownloadResource) Install(downloadDir, tempPath, destDir *paths.Path) error {
// Check the integrity of the package
if ok, err := release.TestLocalArchiveIntegrity(downloadDir); err != nil {
return fmt.Errorf(tr("testing local archive integrity: %s", err))
} else if !ok {
return fmt.Errorf(tr("checking local archive integrity"))
}
// Create a temporary dir to extract package
if err := tempPath.MkdirAll(); err != nil {
return fmt.Errorf(tr("creating temp dir for extraction: %s", err))
}
tempDir, err := tempPath.MkTempDir("package-")
if err != nil {
return fmt.Errorf(tr("creating temp dir for extraction: %s", err))
}
defer tempDir.RemoveAll()
// Obtain the archive path and open it
archivePath, err := release.ArchivePath(downloadDir)
if err != nil {
return fmt.Errorf(tr("getting archive path: %s", err))
}
file, err := os.Open(archivePath.String())
if err != nil {
return fmt.Errorf(tr("opening archive file: %s", err))
}
defer file.Close()
// Extract into temp directory
ctx, cancel := cleanup.InterruptableContext(context.Background())
defer cancel()
if err := extract.Archive(ctx, file, tempDir.String(), nil); err != nil {
return fmt.Errorf(tr("extracting archive: %s", err))
}
// Check package content and find package root dir
root, err := findPackageRoot(tempDir)
if err != nil {
return fmt.Errorf(tr("searching package root dir: %s", err))
}
// Ensure container dir exists
destDirParent := destDir.Parent()
if err := destDirParent.MkdirAll(); err != nil {
return err
}
defer func() {
if empty, err := IsDirEmpty(destDirParent); err == nil && empty {
destDirParent.RemoveAll()
}
}()
// If the destination dir already exists remove it
if destDir.IsDir() {
destDir.RemoveAll()
}
// Move/rename the extracted root directory in the destination directory
if err := root.Rename(destDir); err != nil {
// Copy the extracted root directory to the destination directory, if move failed
if err := root.CopyDirTo(destDir); err != nil {
return fmt.Errorf(tr("moving extracted archive to destination dir: %s", err))
}
}
// TODO
// // Create a package file
// if err := createPackageFile(destDir); err != nil {
// return err
// }
return nil
}
// IsDirEmpty returns true if the directory specified by path is empty.
func IsDirEmpty(path *paths.Path) (bool, error) {
files, err := path.ReadDir()
if err != nil {
return false, err
}
return len(files) == 0, nil
}
func findPackageRoot(parent *paths.Path) (*paths.Path, error) {
files, err := parent.ReadDir()
if err != nil {
return nil, fmt.Errorf(tr("reading package root dir: %s", err))
}
var root *paths.Path
for _, file := range files {
if !file.IsDir() {
continue
}
if root == nil {
root = file
} else {
return nil, fmt.Errorf(tr("no unique root dir in archive, found '%[1]s' and '%[2]s'", root, file))
}
}
if root == nil {
return nil, fmt.Errorf(tr("files in archive must be placed in a subdirectory"))
}
return root, nil
}