From 89f1bc33a2048042a84455fa4c6591b03f574be6 Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Thu, 16 Apr 2026 03:19:04 +0530 Subject: [PATCH 1/3] feat(engine): expose structured commit metadata for pull request evaluation --- internal/engine/commitinfo/commitinfo.go | 45 ++++++++++++++++++ internal/engine/ingester/diff/diff.go | 58 ++++++++++++++++++++++++ internal/providers/github/common.go | 41 +++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 internal/engine/commitinfo/commitinfo.go diff --git a/internal/engine/commitinfo/commitinfo.go b/internal/engine/commitinfo/commitinfo.go new file mode 100644 index 0000000000..7f37a5bfca --- /dev/null +++ b/internal/engine/commitinfo/commitinfo.go @@ -0,0 +1,45 @@ +// 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, + } +} diff --git a/internal/engine/ingester/diff/diff.go b/internal/engine/ingester/diff/diff.go index 4412e58a7f..79c07a6e3d 100644 --- a/internal/engine/ingester/diff/diff.go +++ b/internal/engine/ingester/diff/diff.go @@ -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" @@ -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" @@ -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 @@ -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) @@ -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 diff --git a/internal/providers/github/common.go b/internal/providers/github/common.go index 21a9008cb3..ccf8b34916 100644 --- a/internal/providers/github/common.go +++ b/internal/providers/github/common.go @@ -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, From c2d9b6cb92c36abd583dc440632a93490b537cbf Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Thu, 16 Apr 2026 03:23:42 +0530 Subject: [PATCH 2/3] chore: add license header to commitinfo package --- internal/engine/commitinfo/commitinfo.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/engine/commitinfo/commitinfo.go b/internal/engine/commitinfo/commitinfo.go index 7f37a5bfca..24f202fd35 100644 --- a/internal/engine/commitinfo/commitinfo.go +++ b/internal/engine/commitinfo/commitinfo.go @@ -1,3 +1,6 @@ +// 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. // From ed95b536b882013ac4352b586e0e393e4e5770e8 Mon Sep 17 00:00:00 2001 From: Sachin Kumar Date: Thu, 16 Apr 2026 03:44:19 +0530 Subject: [PATCH 3/3] chore: retrigger CI