forked from git-lfs/git-lfs
/
extension.go
146 lines (127 loc) · 2.88 KB
/
extension.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
package lfs
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"fmt"
"hash"
"io"
"os"
"os/exec"
"strings"
"github.com/git-lfs/git-lfs/config"
)
type pipeRequest struct {
action string
reader io.Reader
fileName string
extensions []config.Extension
}
type pipeResponse struct {
file *os.File
results []*pipeExtResult
}
type pipeExtResult struct {
name string
oidIn string
oidOut string
}
type extCommand struct {
cmd *exec.Cmd
out io.WriteCloser
err *bytes.Buffer
hasher hash.Hash
result *pipeExtResult
}
func pipeExtensions(request *pipeRequest) (response pipeResponse, err error) {
var extcmds []*extCommand
for _, e := range request.extensions {
var pieces []string
switch request.action {
case "clean":
pieces = strings.Split(e.Clean, " ")
case "smudge":
pieces = strings.Split(e.Smudge, " ")
default:
err = fmt.Errorf("Invalid action: " + request.action)
return
}
name := strings.Trim(pieces[0], " ")
var args []string
for _, value := range pieces[1:] {
arg := strings.Replace(value, "%f", request.fileName, -1)
args = append(args, arg)
}
cmd := exec.Command(name, args...)
ec := &extCommand{cmd: cmd, result: &pipeExtResult{name: e.Name}}
extcmds = append(extcmds, ec)
}
hasher := sha256.New()
pipeReader, pipeWriter := io.Pipe()
multiWriter := io.MultiWriter(hasher, pipeWriter)
var input io.Reader
var output io.WriteCloser
input = pipeReader
extcmds[0].cmd.Stdin = input
if response.file, err = TempFile(""); err != nil {
return
}
defer response.file.Close()
output = response.file
last := len(extcmds) - 1
for i, ec := range extcmds {
ec.hasher = sha256.New()
if i == last {
ec.cmd.Stdout = io.MultiWriter(ec.hasher, output)
ec.out = output
continue
}
nextec := extcmds[i+1]
var nextStdin io.WriteCloser
var stdout io.ReadCloser
if nextStdin, err = nextec.cmd.StdinPipe(); err != nil {
return
}
if stdout, err = ec.cmd.StdoutPipe(); err != nil {
return
}
ec.cmd.Stdin = input
ec.cmd.Stdout = io.MultiWriter(ec.hasher, nextStdin)
ec.out = nextStdin
input = stdout
var errBuff bytes.Buffer
ec.err = &errBuff
ec.cmd.Stderr = ec.err
}
for _, ec := range extcmds {
if err = ec.cmd.Start(); err != nil {
return
}
}
if _, err = io.Copy(multiWriter, request.reader); err != nil {
return
}
if err = pipeWriter.Close(); err != nil {
return
}
for _, ec := range extcmds {
if err = ec.cmd.Wait(); err != nil {
if ec.err != nil {
errStr := ec.err.String()
err = fmt.Errorf("Extension '%s' failed with: %s", ec.result.name, errStr)
}
return
}
if err = ec.out.Close(); err != nil {
return
}
}
oid := hex.EncodeToString(hasher.Sum(nil))
for _, ec := range extcmds {
ec.result.oidIn = oid
oid = hex.EncodeToString(ec.hasher.Sum(nil))
ec.result.oidOut = oid
response.results = append(response.results, ec.result)
}
return
}