You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
2.8 KiB
103 lines
2.8 KiB
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"dehub/cmd/dehub/dcmd"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
|
)
|
|
|
|
func cmdHook(ctx context.Context, cmd *dcmd.Cmd) {
|
|
flag := cmd.FlagSet()
|
|
preRcv := flag.Bool("pre-receive", false, "Use dehub as a server-side pre-receive hook")
|
|
|
|
cmd.Run(func() (context.Context, error) {
|
|
|
|
if !*preRcv {
|
|
return nil, errors.New("must set the hook type")
|
|
}
|
|
|
|
repo := ctxRepo(ctx)
|
|
|
|
br := bufio.NewReader(os.Stdin)
|
|
for {
|
|
line, err := br.ReadString('\n')
|
|
if errors.Is(err, io.EOF) {
|
|
return nil, nil
|
|
} else if err != nil {
|
|
return nil, fmt.Errorf("error reading next line from stdin: %w", err)
|
|
}
|
|
fmt.Printf("Processing line %q\n", strings.TrimSpace(line))
|
|
|
|
lineParts := strings.Fields(line)
|
|
if len(lineParts) < 3 {
|
|
return nil, fmt.Errorf("malformed pre-receive hook stdin line %q", line)
|
|
}
|
|
|
|
branchName := plumbing.ReferenceName(lineParts[2])
|
|
|
|
// the zeroRevision gets sent on the very first push
|
|
const zeroRevision plumbing.Revision = "0000000000000000000000000000000000000000"
|
|
|
|
fromRev := plumbing.Revision(lineParts[0])
|
|
var fromHash *plumbing.Hash
|
|
if fromRev != zeroRevision {
|
|
fromHash, err = repo.GitRepo.ResolveRevision(fromRev)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to resolve revision %q: %w", fromRev, err)
|
|
}
|
|
}
|
|
|
|
toRev := plumbing.Revision(lineParts[1])
|
|
toHash, err := repo.GitRepo.ResolveRevision(toRev)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to resolve revision %q: %w", toRev, err)
|
|
}
|
|
|
|
toCommit, err := repo.GitRepo.CommitObject(*toHash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to find commit %q: %w", *toHash, err)
|
|
}
|
|
|
|
var hashesToCheck []plumbing.Hash
|
|
var found bool
|
|
for currCommit := toCommit; ; {
|
|
hashesToCheck = append(hashesToCheck, currCommit.Hash)
|
|
if currCommit.NumParents() == 0 {
|
|
break
|
|
} else if currCommit.NumParents() > 1 {
|
|
return nil, fmt.Errorf("commit %q has more than one parent: %+v",
|
|
currCommit.Hash, currCommit.ParentHashes)
|
|
}
|
|
|
|
parentCommit, err := currCommit.Parent(0)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to get parent of commit %q: %w", currCommit.Hash, err)
|
|
} else if fromHash != nil && parentCommit.Hash == *fromHash {
|
|
found = true
|
|
break
|
|
}
|
|
currCommit = parentCommit
|
|
}
|
|
if !found && fromHash != nil {
|
|
return nil, fmt.Errorf("unable to find commit %q as an ancestor of %q", *fromHash, *toHash)
|
|
}
|
|
|
|
for i := len(hashesToCheck) - 1; i >= 0; i-- {
|
|
hash := hashesToCheck[i]
|
|
fmt.Printf("Verifying change commit %q\n", hash)
|
|
if err := repo.VerifyCommit(branchName, hash); err != nil {
|
|
return nil, fmt.Errorf("could not verify change commit %q: %w", hash, err)
|
|
}
|
|
}
|
|
fmt.Println("All pushed commits have been verified, well done.")
|
|
return nil, nil
|
|
}
|
|
})
|
|
}
|
|
|