git-lfs/lfs/extension.go

182 lines
3.6 KiB
Go
Raw Normal View History

2015-07-10 20:54:06 +00:00
package lfs
import (
2015-07-21 23:53:31 +00:00
"bytes"
"crypto/sha256"
"encoding/hex"
2015-07-10 20:54:06 +00:00
"fmt"
2015-07-21 23:53:31 +00:00
"hash"
"io"
"os"
"os/exec"
2015-07-10 20:54:06 +00:00
"sort"
2015-07-21 23:53:31 +00:00
"strings"
2015-07-10 20:54:06 +00:00
)
// An Extension describes how to manipulate files during smudge and clean.
2015-07-24 04:53:36 +00:00
// Extensions are parsed from the Git config.
2015-07-10 20:54:06 +00:00
type Extension struct {
Name string
Clean string
Smudge string
Priority int
}
2015-07-24 04:53:36 +00:00
type pipeRequest struct {
action string
reader io.Reader
fileName string
extensions []Extension
}
type pipeResponse struct {
file *os.File
results []*pipeExtResult
}
type pipeExtResult struct {
name string
oidIn string
oidOut string
}
2015-07-21 23:53:31 +00:00
type extCommand struct {
cmd *exec.Cmd
out io.WriteCloser
err *bytes.Buffer
hasher hash.Hash
2015-07-24 04:53:36 +00:00
result *pipeExtResult
2015-07-21 23:53:31 +00:00
}
2015-07-10 20:54:06 +00:00
// SortExtensions sorts a map of extensions in ascending order by Priority
func SortExtensions(m map[string]Extension) ([]Extension, error) {
pMap := make(map[int]Extension)
for n, ext := range m {
p := ext.Priority
if _, exist := pMap[p]; exist {
err := fmt.Errorf("duplicate priority %d on %s", p, n)
return nil, err
}
pMap[p] = ext
}
var priorities []int
for p := range pMap {
priorities = append(priorities, p)
}
sort.Ints(priorities)
var result []Extension
for _, p := range priorities {
result = append(result, pMap[p])
}
return result, nil
}
2015-07-21 23:53:31 +00:00
2015-07-24 04:53:36 +00:00
func pipeExtensions(request *pipeRequest) (response pipeResponse, err error) {
2015-07-21 23:53:31 +00:00
var extcmds []*extCommand
2015-07-24 04:53:36 +00:00
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
}
2015-07-21 23:53:31 +00:00
name := strings.Trim(pieces[0], " ")
var args []string
for _, value := range pieces[1:] {
2015-07-24 04:53:36 +00:00
arg := strings.Replace(value, "%f", request.fileName, -1)
2015-07-21 23:53:31 +00:00
args = append(args, arg)
}
cmd := exec.Command(name, args...)
2015-07-24 04:53:36 +00:00
ec := &extCommand{cmd: cmd, result: &pipeExtResult{name: e.Name}}
2015-07-21 23:53:31 +00:00
extcmds = append(extcmds, ec)
}
2015-07-24 04:53:36 +00:00
hasher := sha256.New()
pipeReader, pipeWriter := io.Pipe()
multiWriter := io.MultiWriter(hasher, pipeWriter)
var input io.Reader
2015-07-21 23:53:31 +00:00
var output io.WriteCloser
2015-07-24 04:53:36 +00:00
input = pipeReader
extcmds[0].cmd.Stdin = input
if response.file, err = TempFile(""); err != nil {
2015-07-21 23:53:31 +00:00
return
}
2015-07-24 04:53:36 +00:00
defer response.file.Close()
output = response.file
2015-07-21 23:53:31 +00:00
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
}
}
2015-07-24 04:53:36 +00:00
if _, err = io.Copy(multiWriter, request.reader); err != nil {
return
}
if err = pipeWriter.Close(); err != nil {
return
}
2015-07-21 23:53:31 +00:00
for _, ec := range extcmds {
if err = ec.cmd.Wait(); err != nil {
2015-07-24 04:53:36 +00:00
if ec.err != nil {
errStr := ec.err.String()
err = fmt.Errorf("Extension '%s' failed with: %s", ec.result.name, errStr)
}
2015-07-21 23:53:31 +00:00
return
}
if err = ec.out.Close(); err != nil {
return
}
}
2015-07-24 04:53:36 +00:00
oid := hex.EncodeToString(hasher.Sum(nil))
2015-07-21 23:53:31 +00:00
for _, ec := range extcmds {
2015-07-24 04:53:36 +00:00
ec.result.oidIn = oid
oid = hex.EncodeToString(ec.hasher.Sum(nil))
ec.result.oidOut = oid
response.results = append(response.results, ec.result)
2015-07-21 23:53:31 +00:00
}
return
}