dd8e306e31
When our go.mod file was introduced in commit 114e85c2002091eb415040923d872f8e4a4bc636 in PR #3208, the module path chosen did not include a trailing /v2 component. However, the Go modules specification now advises that module paths must have a "major version suffix" which matches the release version. We therefore add a /v2 suffix to our module path and all its instances in import paths. See also https://golang.org/ref/mod#major-version-suffixes for details regarding the Go module system's major version suffix rule.
163 lines
3.4 KiB
Go
163 lines
3.4 KiB
Go
package lfs
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/git-lfs/git-lfs/v2/config"
|
|
"github.com/git-lfs/git-lfs/v2/subprocess"
|
|
)
|
|
|
|
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 *subprocess.Cmd
|
|
out io.WriteCloser
|
|
err *bytes.Buffer
|
|
hasher hash.Hash
|
|
result *pipeExtResult
|
|
}
|
|
|
|
func pipeExtensions(cfg *config.Configuration, request *pipeRequest) (response pipeResponse, err error) {
|
|
var extcmds []*extCommand
|
|
defer func() {
|
|
// In the case of an early return before the end of this
|
|
// function (in response to an error, etc), kill all running
|
|
// processes. Errors are ignored since the function has already
|
|
// returned.
|
|
//
|
|
// In the happy path, the commands will have already been
|
|
// `Wait()`-ed upon and e.cmd.Process.Kill() will return an
|
|
// error, but we can ignore it.
|
|
for _, e := range extcmds {
|
|
if e.cmd.Process != nil {
|
|
e.cmd.Process.Kill()
|
|
}
|
|
}
|
|
}()
|
|
|
|
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 := subprocess.ExecCommand(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(cfg, ""); 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
|
|
}
|