Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions internal/engine/commitinfo/commitinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: Copyright 2023 The Minder Authors
// SPDX-License-Identifier: Apache-2.0

// Package commitinfo provides utilities for extracting and normalizing
// pull request commit metadata into a provider-agnostic structure.
//
// It is designed to decouple commit data handling from provider-specific
// implementations (e.g., GitHub) and avoid import cycles between engine
// components.
package commitinfo

import (
"strings"

"github.com/google/go-github/v63/github"
)

// CommitInfo represents normalized pull request commit metadata.
type CommitInfo struct {
SHA string
Message string
Author string
}

// Extract normalizes a GitHub commit into a CommitInfo struct.
func Extract(c *github.RepositoryCommit) CommitInfo {
msg := ""
if c.GetCommit() != nil {
msg = c.GetCommit().GetMessage()
}

firstLine := msg
if idx := strings.Index(msg, "\n"); idx != -1 {
firstLine = msg[:idx]
}
firstLine = strings.TrimSpace(firstLine)

author := ""
if c.GetCommit() != nil && c.GetCommit().Author != nil {
author = c.GetCommit().Author.GetName()
}

return CommitInfo{
SHA: c.GetSHA(),
Message: firstLine,
Author: author,
}
}
41 changes: 41 additions & 0 deletions internal/engine/commitinfo/commitinfo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2023 The Minder Authors
// SPDX-License-Identifier: Apache-2.0

package commitinfo

import (
"testing"

"github.com/google/go-github/v63/github"
)

func TestExtract_Basic(t *testing.T) {
t.Parallel()

msg := "feat: add new feature\n\nmore details"
author := "Sachin"

c := &github.RepositoryCommit{
SHA: github.String("abc123"),
Commit: &github.Commit{
Message: github.String(msg),
Author: &github.CommitAuthor{
Name: github.String(author),
},
},
}

info := Extract(c)

if info.SHA != "abc123" {
t.Fatalf("expected SHA abc123, got %s", info.SHA)
}

if info.Message != "feat: add new feature" {
t.Fatalf("unexpected message: %s", info.Message)
}

if info.Author != author {
t.Fatalf("expected author %s, got %s", author, info.Author)
}
}
58 changes: 58 additions & 0 deletions internal/engine/ingester/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/helper/iofs"
"github.com/google/go-github/v63/github"
scalibr "github.com/google/osv-scalibr"
"github.com/google/osv-scalibr/extractor"
scalibr_fs "github.com/google/osv-scalibr/fs"
Expand All @@ -27,6 +28,7 @@ import (
"github.com/rs/zerolog"
"google.golang.org/protobuf/reflect/protoreflect"

"github.com/mindersec/minder/internal/engine/commitinfo"
pbinternal "github.com/mindersec/minder/internal/proto"
pb "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
"github.com/mindersec/minder/pkg/engine/v1/interfaces"
Expand All @@ -37,9 +39,21 @@ const (
// DiffRuleDataIngestType is the type of the diff rule data ingest engine
DiffRuleDataIngestType = "diff"
prFilesPerPage = 30
prCommitsPerPage = 30
wildcard = "*"
)

type commitLister interface {
ListPullRequestCommits(
ctx context.Context,
owner string,
repo string,
prNumber int,
perPage int,
pageNumber int,
) ([]*github.RepositoryCommit, *github.Response, error)
}

// Diff is the diff rule data ingest engine
type Diff struct {
cli interfaces.GitHubListAndClone
Expand Down Expand Up @@ -92,6 +106,10 @@ func (di *Diff) Ingest(
}
prNumber := int(pr.Number)

if err := di.logPullRequestCommits(ctx, pr, prNumber); err != nil {
return nil, fmt.Errorf("error listing pull request commits: %w", err)
}

switch di.cfg.GetType() {
case "", pb.DiffTypeDep:
return di.getDepTypeDiff(ctx, prNumber, pr)
Expand All @@ -108,6 +126,46 @@ func (di *Diff) Ingest(
}
}

func (di *Diff) logPullRequestCommits(ctx context.Context, pr *pbinternal.PullRequest, prNumber int) error {
lister, ok := di.cli.(commitLister)
if !ok {
return nil
}

logger := zerolog.Ctx(ctx)
page := 0
for {
commits, resp, err := lister.ListPullRequestCommits(
ctx,
pr.RepoOwner,
pr.RepoName,
prNumber,
prCommitsPerPage,
page,
)
if err != nil {
return fmt.Errorf("failed to list pull request commits: %w", err)
}

for _, c := range commits {
info := commitinfo.Extract(c)
logger.Debug().
Str("commit_sha", info.SHA).
Str("commit_msg", info.Message).
Str("commit_author", info.Author).
Msg("pull request commit")
}

if resp == nil || resp.NextPage == 0 {
break
}

page = resp.NextPage
}

return nil
}

func (di *Diff) getDepTypeDiff(ctx context.Context, prNumber int, pr *pbinternal.PullRequest) (*interfaces.Ingested, error) {
deps := pbinternal.PrDependencies{Pr: pr}
page := 0
Expand Down
41 changes: 41 additions & 0 deletions internal/providers/github/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,47 @@ func (c *GitHub) ListFiles(
return resp.files, resp.resp, err
}

// ListPullRequestCommits is a wrapper for the GitHub API to list commits in a pull request.
func (c *GitHub) ListPullRequestCommits(
ctx context.Context,
owner string,
repo string,
prNumber int,
perPage int,
pageNumber int,
) ([]*github.RepositoryCommit, *github.Response, error) {
type listPullRequestCommitsRespWrapper struct {
commits []*github.RepositoryCommit
resp *github.Response
}

op := func() (listPullRequestCommitsRespWrapper, error) {
opt := &github.ListOptions{
Page: pageNumber,
PerPage: perPage,
}
commits, resp, err := c.client.PullRequests.ListCommits(ctx, owner, repo, prNumber, opt)

listCommitsResp := listPullRequestCommitsRespWrapper{
commits: commits,
resp: resp,
}

if isRateLimitError(err) {
waitErr := c.waitForRateLimitReset(ctx, err)
if waitErr == nil {
return listCommitsResp, err
}
return listCommitsResp, backoffv4.Permanent(err)
}

return listCommitsResp, backoffv4.Permanent(err)
}

resp, err := performWithRetry(ctx, op)
return resp.commits, resp.resp, err
}

// CreateReview is a wrapper for the GitHub API to create a review
func (c *GitHub) CreateReview(
ctx context.Context, owner, repo string, number int, reviewRequest *github.PullRequestReviewRequest,
Expand Down
Loading