1
0
mirror of https://github.com/SAP/jenkins-library.git synced 2024-12-14 11:03:09 +02:00
sap-jenkins-library/pkg/transportrequest/gitutils.go

110 lines
3.5 KiB
Go

package transportrequest
import (
"fmt"
gitUtils "github.com/SAP/jenkins-library/pkg/git"
"github.com/SAP/jenkins-library/pkg/log"
"github.com/SAP/jenkins-library/pkg/piperutils"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/pkg/errors"
"os"
"regexp"
"sort"
"strings"
)
var logRange = gitUtils.LogRange
var findLabelsInCommits = FindLabelsInCommits
type iTransportRequestGitUtils interface {
PlainOpen(directory string) (*git.Repository, error)
}
type transportRequestGitUtils struct {
}
func (g *transportRequestGitUtils) PlainOpen(directory string) (*git.Repository, error) {
r, err := gitUtils.PlainOpen(directory)
if err != nil {
return nil, errors.Wrapf(err, "Unable to open git repository at '%s'", directory)
}
return r, nil
}
// FindIDInRange finds a ID according to the label in a commit range <from>..<to>.
// We assume the git repo is present in the current working directory.
func FindIDInRange(label, from, to string) (string, error) {
return findIDInRange(label, from, to, &transportRequestGitUtils{})
}
func findIDInRange(label, from, to string, trGitUtils iTransportRequestGitUtils) (string, error) {
workdir, err := os.Getwd()
if err != nil {
return "", errors.Wrapf(err, "Cannot open git repo in current working directory '%s'", workdir)
}
log.Entry().Infof("Opening git repo at '%s'", workdir)
r, err := trGitUtils.PlainOpen(workdir)
if err != nil {
return "", errors.Wrapf(err, "Unable to open git repository at '%s'", workdir)
}
cIter, err := logRange(r, from, to)
if err != nil {
return "", errors.Wrapf(err, "Cannot retrieve '%s'. Unable to resolve commits in range '%s..%s'", label, from, to)
}
ids, err := findLabelsInCommits(cIter, label)
if err != nil {
return "", errors.Wrapf(err, "Cannot retrieve '%s'. Unable to traverse commits in range '%s..%s'", label, from, to)
}
if len(ids) > 1 {
return "", fmt.Errorf("More than one values found for label '%s' in range '%s..%s': '%s'", label, from, to, ids)
}
if len(ids) == 0 {
return "", fmt.Errorf("No values found for '%s' in range '%s..%s'", label, from, to)
}
return ids[0], nil
}
// FindLabelsInCommits a label is considered to be something like
// key: label, e.g. TransportRequest: 123456
// These labels are expected to be contained in the git commit message as
// a separate line in the commit message body.
// In case several labels are found they are returned in ascending order.
func FindLabelsInCommits(commits object.CommitIter, label string) ([]string, error) {
labelRegex, err := regexp.Compile(finishLabel(label))
if err != nil {
return []string{}, fmt.Errorf("Cannot extract label: %w", err)
}
ids := []string{}
err = commits.ForEach(func(c *object.Commit) error {
for _, e := range labelRegex.FindAllStringSubmatch(c.Message, -1) {
if len(e) < 2 { // the first entry is the full match, the second entry (at index 1) is the group
return fmt.Errorf("Cannot extract label '%s' from commit '%s': '%s'", label, c.ID(), c.Message)
}
ids = append(ids, e[1])
}
return nil
})
if err != nil {
return []string{}, fmt.Errorf("Cannot extract label: %w", err)
}
labels := piperutils.UniqueStrings(ids)
sort.Strings(labels)
return labels, nil
}
func finishLabel(label string) string {
// contains prefix, like the old default
if strings.ContainsAny(label, ":=") {
return fmt.Sprintf(`(?m)^\s*%s\s*(\S*)\s*$`, label)
}
// contains key only, like the new default
return fmt.Sprintf(`(?m)^\s*%s\s*:\s*(\S*)\s*$`, label)
}