dco-go/main.go
Evgenii Stratonikov 2f1de6fc41 Use merge-base as a starting point
Set it to target branch and check only the commits we are interested in.
If the commit is already in the branch, we just get an old behaviour.

Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2023-07-13 11:51:07 +03:00

111 lines
2.4 KiB
Go

package main
import (
"errors"
"fmt"
"log"
"os"
"regexp"
"strings"
git "github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
gha "github.com/sethvargo/go-githubactions"
)
func main() {
// Prepare regexp templates.
rxHeader := regexp.MustCompile(`^(\[\#[0-9Xx]+\]\s|Release)`)
rxSignOff := regexp.MustCompile(`^Signed-off-by:`)
// Open current git dir.
r, err := git.PlainOpen("./")
if err != nil {
log.Fatalf("Failed to open repository: %v", err)
}
// Retrieve the commit history.
head, err := r.Head()
if err != nil {
log.Fatalf("Failed to retrieve HEAD reference: %v", err)
}
// Create iterator over commits.
commits, err := r.Log(&git.LogOptions{From: head.Hash()})
if err != nil {
log.Fatalf("Failed to retrieve commit history: %v", err)
}
// Limit number of iterations.
var lca *object.Commit
if from := gha.GetInput("from"); from != "" {
lca = getMergeBase(r, head, from)
}
// Processing result.
var fail bool
_ = commits.ForEach(func(c *object.Commit) error {
// Stop iterator when limit is reached.
if lca != nil && c.Hash == lca.Hash {
return errors.New("stop")
}
// Parse commit data.
id := c.ID().String()[:7]
lines := strings.Split(strings.Trim(c.Message, "\n"), "\n")
// Do not process empty commit.
if len(lines) == 0 {
fail = true
fmt.Printf("Error: empty commit [%s]\n", id)
return nil
}
// Check commit header.
header := lines[0]
if !rxHeader.MatchString(header) {
fail = true
fmt.Printf("Error: invalid header %s [%s]\n", header, id)
return nil
}
// Check commit sign-off.
if !rxSignOff.MatchString(lines[len(lines)-1]) {
fail = true
fmt.Printf("Error: missing sign-off %s [%s]\n", header, id)
}
return nil
})
// Return non-zero code if DCO check failed.
if fail {
os.Exit(1)
}
}
func getMergeBase(r *git.Repository, head *plumbing.Reference, from string) *object.Commit {
h, err := r.ResolveRevision(plumbing.Revision(from))
if err != nil {
log.Fatalf("Failed to resolve a reference: %v", err)
}
to, err := r.CommitObject(*h)
if err != nil {
log.Fatalf("Failed to get commit object: %v", err)
}
other, err := r.CommitObject(head.Hash())
if err != nil {
log.Fatalf("Failed to get HEAD commit object: %v", err)
}
cc, err := to.MergeBase(other)
if err != nil {
log.Fatalf("Failed to determine merge-base: %v", err)
}
return cc[0]
}