-
Notifications
You must be signed in to change notification settings - Fork 4
/
file_system_writer.go
135 lines (111 loc) · 2.78 KB
/
file_system_writer.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
package config
import (
"bytes"
"crypto/sha256"
"fmt"
"hash"
"io"
"os"
"path/filepath"
"strings"
"github.com/spf13/afero"
)
type FileSystemWriter struct {
RootDir string
Files []*File
fs afero.Afero
hash hash.Hash
}
type OptionFn func(v *FileSystemWriter)
func WithHasher(h hash.Hash) OptionFn {
return func(v *FileSystemWriter) {
v.hash = h
}
}
func NewFileSystemWriter(fs afero.Fs, opts ...OptionFn) *FileSystemWriter {
v := &FileSystemWriter{
fs: afero.Afero{Fs: fs},
}
for _, opt := range opts {
opt(v)
}
if v.hash == nil {
v.hash = sha256.New()
}
return v
}
func (v *FileSystemWriter) Commit(root string, files []*File) (string, []*LocalFile, error) {
dir, err := v.checkRoot(root)
if err != nil {
return "", nil, err
}
if len(files) == 0 {
return "", nil, nil
}
fs := afero.Afero{Fs: afero.NewBasePathFs(v.fs.Fs, dir)}
localFiles := make([]*LocalFile, 0, len(files))
for _, of := range files {
lf, err := v.checkFile(fs, of)
if err != nil {
return "", nil, err
}
localFiles = append(localFiles, lf)
}
return dir, localFiles, nil
}
func (v *FileSystemWriter) checkFile(fs afero.Afero, n *File) (*LocalFile, error) {
if filepath.IsAbs(n.Path) {
return nil, fmt.Errorf("file.path error: %q file must be relative path: %q", n.Name, n.Path)
}
// check / create dir
dir := filepath.Dir(n.Path)
if ok, err := fs.DirExists(dir); err != nil {
return nil, fmt.Errorf("DirExists error: %q: %w", dir, err)
} else if !ok {
if err := fs.MkdirAll(dir, os.ModePerm); err != nil {
return nil, fmt.Errorf("file.path error: unable to create dir %q: %w", dir, err)
}
}
// check / create file
var dstHash []byte
if ok, err := fs.Exists(n.Path); err != nil {
return nil, fmt.Errorf("exists error: %q: %w", n.Path, err)
} else if ok {
v.hash.Reset()
if file, err := fs.Open(n.Path); err == nil {
_, _ = io.Copy(v.hash, file)
}
dstHash = v.hash.Sum(nil)
}
fullPath, err := fs.Fs.(*afero.BasePathFs).RealPath(n.Path)
if err != nil {
return nil, fmt.Errorf("RealPath error: %q: %w", n.Path, err)
}
lf := &LocalFile{Name: n.Name, FullPath: fullPath}
v.hash.Reset()
_, _ = io.Copy(v.hash, strings.NewReader(n.Content))
lf.Hash = v.hash.Sum(nil)
if !bytes.Equal(lf.Hash, dstHash) {
if err := fs.WriteFile(n.Path, []byte(n.Content), os.ModePerm); err != nil {
return nil, fmt.Errorf("file write: unable to write file %q: %w", n.Path, err)
}
}
return lf, nil
}
func (v *FileSystemWriter) checkRoot(dir string) (string, error) {
if !filepath.IsAbs(dir) {
var err error
dir, err = filepath.Abs(dir)
if err != nil {
return "", err
}
}
if ok, err := v.fs.DirExists(dir); err != nil {
return "", err
} else if !ok {
if err := v.fs.MkdirAll(dir, os.ModePerm); err != nil {
return "", err
}
}
return dir, nil
}