Skip to content

Commit af651a1

Browse files
committed
Moved install/checksum functions in releases package
1 parent 6c3328a commit af651a1

File tree

5 files changed

+182
-213
lines changed

5 files changed

+182
-213
lines changed

commands/validate/validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ import (
3737

3838
"github.com/bcmi-labs/arduino-cli/commands"
3939
"github.com/bcmi-labs/arduino-cli/common/formatter"
40+
"github.com/bcmi-labs/arduino-cli/common/releases"
4041
"github.com/bcmi-labs/arduino-cli/configs"
41-
"github.com/bcmi-labs/arduino-cli/cores"
4242
"github.com/sirupsen/logrus"
4343
"github.com/spf13/cobra"
4444
)
@@ -77,7 +77,7 @@ func run(cmd *cobra.Command, args []string) {
7777
}
7878
pathParts := strings.Split(relativePath, string(filepath.Separator))
7979
if len(pathParts) == 4 {
80-
isValid, err := cores.CheckDirChecksum(path)
80+
isValid, err := releases.CheckDirChecksum(path)
8181
if err != nil {
8282
return err
8383
}

common/releases/checksums.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,16 @@ package releases
3232
import (
3333
"bytes"
3434
"crypto"
35+
"crypto/sha256"
3536
"encoding/hex"
37+
"encoding/json"
3638
"errors"
39+
"fmt"
3740
"hash"
3841
"io"
42+
"io/ioutil"
3943
"os"
44+
"path/filepath"
4045
"strings"
4146
)
4247

@@ -100,3 +105,63 @@ func checkLocalArchive(release *DownloadResource) error {
100105
}
101106
return nil
102107
}
108+
109+
const (
110+
filePermissions = 0644
111+
packageFileName = "package.json"
112+
)
113+
114+
type packageFile struct {
115+
Checksum string `json:"checksum"`
116+
}
117+
118+
func computeDirChecksum(root string) (string, error) {
119+
hash := sha256.New()
120+
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
121+
if err != nil || info.IsDir() || (info.Name() == packageFileName && filepath.Dir(path) == root) {
122+
return nil
123+
}
124+
f, err := os.Open(path)
125+
if err != nil {
126+
return nil
127+
}
128+
defer f.Close()
129+
if _, err := io.Copy(hash, f); err != nil {
130+
return fmt.Errorf("failed to compute hash of file \"%s\"", info.Name())
131+
}
132+
return nil
133+
})
134+
if err != nil {
135+
return "", err
136+
}
137+
return fmt.Sprintf("%x", hash.Sum(nil)), nil
138+
}
139+
140+
func createPackageFile(root string) error {
141+
checksum, err := computeDirChecksum(root)
142+
if err != nil {
143+
return err
144+
}
145+
146+
packageJSON, _ := json.Marshal(packageFile{checksum})
147+
err = ioutil.WriteFile(filepath.Join(root, packageFileName), packageJSON, filePermissions)
148+
if err != nil {
149+
return err
150+
}
151+
return nil
152+
}
153+
154+
// CheckDirChecksum reads checksum from the package.json and compares it with a recomputed value.
155+
func CheckDirChecksum(root string) (bool, error) {
156+
packageJSON, err := ioutil.ReadFile(filepath.Join(root, packageFileName))
157+
if err != nil {
158+
return false, err
159+
}
160+
var file packageFile
161+
json.Unmarshal(packageJSON, &file)
162+
checksum, err := computeDirChecksum(root)
163+
if err != nil {
164+
return false, err
165+
}
166+
return file.Checksum == checksum, nil
167+
}

common/releases/install.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package releases
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"io/ioutil"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/codeclysm/extract"
11+
)
12+
13+
// Install installs the resource in three steps:
14+
// - the archive is unpacked in a temporary subfolder of tempPath
15+
// - there should be only one root folder in the unpacked content
16+
// - the only root folder is moved/renamed to/as the destination directory
17+
// Note that tempPath and destDir must be on the same filesystem partition
18+
// otherwise the last step will fail.
19+
func (release *DownloadResource) Install(tempPath string, destDir string) error {
20+
// Create a temporary folder to extract package
21+
if err := os.MkdirAll(tempPath, 0777); err != nil {
22+
return fmt.Errorf("creating temp dir for extraction: %s", err)
23+
}
24+
tempDir, err := ioutil.TempDir(tempPath, "package-")
25+
if err != nil {
26+
return fmt.Errorf("creating temp dir for extraction: %s", err)
27+
}
28+
defer os.RemoveAll(tempDir)
29+
30+
// Obtain the archive path and open it
31+
archivePath, err := release.ArchivePath()
32+
if err != nil {
33+
return fmt.Errorf("getting archive path: %s", err)
34+
}
35+
file, err := os.Open(archivePath)
36+
if err != nil {
37+
return fmt.Errorf("opening archive file: %s", err)
38+
}
39+
defer file.Close()
40+
41+
// Extract into temp directory
42+
if err := extract.Archive(file, tempDir, nil); err != nil {
43+
return fmt.Errorf("extracting archive: %s", err)
44+
}
45+
46+
// Check package content and find package root dir
47+
root, err := findPackageRoot(tempDir)
48+
if err != nil {
49+
return fmt.Errorf("searching package root dir: %s", err)
50+
}
51+
52+
// Ensure container dir exists
53+
destDirParent := filepath.Dir(destDir)
54+
if err := os.MkdirAll(destDirParent, 0777); err != nil {
55+
return err
56+
}
57+
defer func() {
58+
if empty, err := IsDirEmpty(destDirParent); err == nil && empty {
59+
os.RemoveAll(destDirParent)
60+
}
61+
}()
62+
63+
// Move/rename the extracted root directory in the destination directory
64+
if err := os.Rename(root, destDir); err != nil {
65+
return err
66+
}
67+
68+
// Create a package file
69+
if err := createPackageFile(destDir); err != nil {
70+
return err
71+
}
72+
73+
return nil
74+
}
75+
76+
// IsDirEmpty returns true if the directory specified by path is empty.
77+
func IsDirEmpty(path string) (bool, error) {
78+
f, err := os.Open(path)
79+
if err != nil {
80+
return false, err
81+
}
82+
defer f.Close()
83+
84+
// read in ONLY one file
85+
_, err = f.Readdir(1)
86+
87+
// and if the file is EOF... well, the dir is empty.
88+
if err == io.EOF {
89+
return true, nil
90+
}
91+
return false, err
92+
}
93+
94+
func findPackageRoot(parent string) (string, error) {
95+
files, err := ioutil.ReadDir(parent)
96+
if err != nil {
97+
return "", fmt.Errorf("reading package root dir: %s", err)
98+
}
99+
root := ""
100+
for _, fileInfo := range files {
101+
if !fileInfo.IsDir() {
102+
continue
103+
}
104+
if root == "" {
105+
root = fileInfo.Name()
106+
} else {
107+
return "", fmt.Errorf("no unique root dir in archive, found '%s' and '%s'", root, fileInfo.Name())
108+
}
109+
}
110+
return filepath.Join(parent, root), nil
111+
}

cores/checksum.go

Lines changed: 0 additions & 100 deletions
This file was deleted.

0 commit comments

Comments
 (0)