generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 7
/
filehash.go
150 lines (127 loc) · 3.36 KB
/
filehash.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
139
140
141
142
143
144
145
146
147
148
149
150
package buildengine
import (
"bytes"
"crypto/sha256"
"io"
"io/fs"
"os"
"path/filepath"
"github.com/bmatcuk/doublestar/v4"
)
type FileChangeType rune
func (f FileChangeType) String() string { return string(f) }
func (f FileChangeType) GoString() string {
switch f {
case FileAdded:
return "buildengine.FileAdded"
case FileRemoved:
return "buildengine.FileRemoved"
case FileChanged:
return "buildengine.FileChanged"
default:
panic("unknown file change type")
}
}
const (
FileAdded FileChangeType = '+'
FileRemoved FileChangeType = '-'
FileChanged FileChangeType = '*'
)
type FileHashes map[string][]byte
// CompareFileHashes compares the hashes of the files in the oldFiles and newFiles maps.
//
// Returns true if the hashes are equal, false otherwise.
//
// If false, the returned string will be a file that caused the difference and the
// returned FileChangeType will be the type of change that occurred.
func CompareFileHashes(oldFiles, newFiles FileHashes) (FileChangeType, string, bool) {
for key, hash1 := range oldFiles {
hash2, exists := newFiles[key]
if !exists {
return FileRemoved, key, false
}
if !bytes.Equal(hash1, hash2) {
return FileChanged, key, false
}
}
for key := range newFiles {
if _, exists := oldFiles[key]; !exists {
return FileAdded, key, false
}
}
return ' ', "", true
}
// ComputeFileHashes computes the SHA256 hash of all (non-git-ignored) files in
// the given directory.
func ComputeFileHashes(project Project) (FileHashes, error) {
config := project.Config()
fileHashes := make(FileHashes)
rootDirs := computeRootDirs(config.Dir, config.Watch)
for _, rootDir := range rootDirs {
err := WalkDir(rootDir, func(srcPath string, entry fs.DirEntry) error {
if entry.IsDir() {
return nil
}
hash, matched, err := ComputeFileHash(rootDir, srcPath, config.Watch)
if err != nil {
return err
}
if !matched {
return nil
}
fileHashes[srcPath] = hash
return nil
})
if err != nil {
return nil, err
}
}
return fileHashes, nil
}
func ComputeFileHash(baseDir, srcPath string, watch []string) (hash []byte, matched bool, err error) {
for _, pattern := range watch {
relativePath, err := filepath.Rel(baseDir, srcPath)
if err != nil {
return nil, false, err
}
match, err := doublestar.PathMatch(pattern, relativePath)
if err != nil {
return nil, false, err
}
if match {
file, err := os.Open(srcPath)
if err != nil {
return nil, false, err
}
hasher := sha256.New()
if _, err := io.Copy(hasher, file); err != nil {
_ = file.Close()
return nil, false, err
}
hash := hasher.Sum(nil)
if err := file.Close(); err != nil {
return nil, false, err
}
return hash, true, nil
}
}
return nil, false, nil
}
// computeRootDirs computes the unique root directories for the given baseDir and patterns.
func computeRootDirs(baseDir string, patterns []string) []string {
uniqueRoots := make(map[string]struct{})
uniqueRoots[baseDir] = struct{}{}
for _, pattern := range patterns {
fullPath := filepath.Join(baseDir, pattern)
dirPath, _ := doublestar.SplitPattern(fullPath)
cleanedPath := filepath.Clean(dirPath)
if _, err := os.Stat(cleanedPath); err == nil {
uniqueRoots[cleanedPath] = struct{}{}
}
}
roots := make([]string, 0, len(uniqueRoots))
for root := range uniqueRoots {
roots = append(roots, root)
}
return roots
}