Skip to content

Commit f6ef09d

Browse files
mjuragaaiharos
authored andcommitted
MEDIUM: commit: fetch the commits via API
Changed fetching of the PR/MR commits via respective APIs rather then dealing with git hashes.
1 parent 6613ef5 commit f6ef09d

File tree

3 files changed

+458
-141
lines changed

3 files changed

+458
-141
lines changed

check-commit/check.go

Lines changed: 87 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"encoding/hex"
56
"errors"
67
"fmt"
@@ -9,13 +10,15 @@ import (
910
"os"
1011
"path"
1112
"regexp"
13+
"strconv"
1214
"strings"
1315
"unicode"
1416
"unicode/utf8"
1517

16-
"github.com/go-git/go-git/v5"
17-
"github.com/go-git/go-git/v5/plumbing"
18-
"github.com/go-git/go-git/v5/plumbing/object"
18+
"github.com/google/go-github/v35/github"
19+
20+
"github.com/xanzy/go-gitlab"
21+
"golang.org/x/oauth2"
1922
yaml "gopkg.in/yaml.v2"
2023
)
2124

@@ -78,6 +81,7 @@ TagOrder:
7881
MAXSUBJECTLEN = 100
7982

8083
GITHUB = "Github"
84+
GITLAB = "Gitlab"
8185
)
8286

8387
var ErrSubjectMessageFormat = errors.New("invalid subject message format")
@@ -208,44 +212,45 @@ func (c CommitPolicyConfig) IsEmpty() bool {
208212
}
209213

210214
type gitEnv struct {
211-
EnvName string
212-
Event string
213-
Ref string
214-
Base string
215+
EnvName string
216+
URL string
217+
Token string
218+
ProjectID string
219+
PMRequestID string
215220
}
216221

217222
type gitEnvVars struct {
218-
EnvName string
219-
EventVar string
220-
RefVar string
221-
BaseVar string
223+
EnvName string
224+
ApiUrl string
225+
ApiToken string
226+
ProjectID string
227+
RequestID string
222228
}
223229

224230
var ErrGitEnvironment = errors.New("git environment error")
225231

226232
func readGitEnvironment() (*gitEnv, error) {
227233
knownVars := []gitEnvVars{
228-
{GITHUB, "GITHUB_EVENT_NAME", "GITHUB_SHA", "GITHUB_BASE_REF"},
229-
{"Gitlab", "CI_PIPELINE_SOURCE", "CI_MERGE_REQUEST_SOURCE_BRANCH_NAME", "CI_MERGE_REQUEST_TARGET_BRANCH_NAME"},
230-
{"Gitlab-commit", "CI_PIPELINE_SOURCE", "CI_COMMIT_SHA", "CI_DEFAULT_BRANCH"},
234+
{GITHUB, "GITHUB_API_URL", "GITHUB_TOKEN", "GITHUB_REPOSITORY", "GITHUB_SHA"},
235+
{GITLAB, "CI_API_V4_URL", "CI_JOB_TOKEN", "CI_MERGE_REQUEST_PROJECT_ID", "CI_MERGE_REQUEST_ID"},
231236
}
232237

233-
var ref, base string
234-
235238
for _, vars := range knownVars {
236-
event := os.Getenv(vars.EventVar)
237-
ref = os.Getenv(vars.RefVar)
238-
base = os.Getenv(vars.BaseVar)
239+
url := os.Getenv(vars.ApiUrl)
240+
token := os.Getenv(vars.ApiToken)
241+
project := os.Getenv(vars.ProjectID)
242+
request := os.Getenv(vars.RequestID)
239243

240-
if !(ref == "" && base == "") || (vars.EnvName == GITHUB && event == "push") {
244+
if !(url == "" && token == "" && project == "" && request == "") {
241245
log.Printf("detected %s environment\n", vars.EnvName)
242-
log.Printf("using event '%s' with refs '%s' and '%s'\n", event, ref, base)
246+
log.Printf("using api url '%s'\n", url)
243247

244248
return &gitEnv{
245-
EnvName: vars.EnvName,
246-
Event: event,
247-
Ref: ref,
248-
Base: base,
249+
EnvName: vars.EnvName,
250+
URL: url,
251+
Token: token,
252+
ProjectID: project,
253+
PMRequestID: request,
249254
}, nil
250255
}
251256
}
@@ -273,89 +278,86 @@ func LoadCommitPolicy(filename string) (CommitPolicyConfig, error) {
273278
return commitPolicy, nil
274279
}
275280

276-
func hashesFromRefs(repo *git.Repository, repoEnv *gitEnv) ([]*plumbing.Hash, []*object.Commit) {
277-
var refStrings []string
278-
refStrings = append(refStrings, repoEnv.Ref)
279-
280-
if !(repoEnv.EnvName == GITHUB && repoEnv.Event == "push") { // for Github push we only have the last commit
281-
refStrings = append(refStrings, fmt.Sprintf("refs/remotes/origin/%s", repoEnv.Base))
282-
}
281+
func getGithubCommitSubjects(token string, repo string, sha string) ([]string, error) {
282+
ctx := context.Background()
283283

284-
hashes := make([]*plumbing.Hash, 0, 2)
284+
ts := oauth2.StaticTokenSource(
285+
&oauth2.Token{AccessToken: token},
286+
)
287+
tc := oauth2.NewClient(ctx, ts)
288+
githubClient := github.NewClient(tc)
285289

286-
for _, refString := range refStrings {
287-
hash, err := repo.ResolveRevision(plumbing.Revision(refString))
288-
if err != nil {
289-
log.Fatalf("unable to resolve revision %s to hash: %s", refString, err)
290-
}
290+
repoSlice := strings.SplitN(repo, "/", 2)
291291

292-
hashes = append(hashes, hash)
292+
prs, _, err := githubClient.PullRequests.ListPullRequestsWithCommit(ctx, repoSlice[0], repoSlice[1], sha, &github.PullRequestListOptions{})
293+
if err != nil {
294+
return nil, fmt.Errorf("error fetching prs for commit %s: %w", sha, err)
293295
}
294296

295-
commits := make([]*object.Commit, 0, 2)
296-
297-
for _, hash := range hashes {
298-
commit, err := repo.CommitObject(*hash)
297+
subjects := []string{}
298+
if len(prs) > 0 {
299+
// Check the latest PR with this commit
300+
prNo := prs[0].GetNumber()
301+
commits, _, err := githubClient.PullRequests.ListCommits(ctx, repoSlice[0], repoSlice[1], prNo, &github.ListOptions{})
299302
if err != nil {
300-
log.Fatalf("unable to find commit %s", hash.String())
303+
return nil, fmt.Errorf("error fetching commits: %w", err)
304+
}
305+
for _, c := range commits {
306+
l := strings.SplitN(c.Commit.GetMessage(), "\n", 2)
307+
if len(l) > 0 {
308+
subjects = append(subjects, l[0])
309+
}
310+
}
311+
} else {
312+
// no PRs, event was a direct push, check only latest commit
313+
c, _, err := githubClient.Repositories.GetCommit(ctx, repoSlice[0], repoSlice[1], sha)
314+
if err != nil {
315+
return nil, fmt.Errorf("error fetching commit %s: %w", sha, err)
316+
}
317+
l := strings.SplitN(c.Commit.GetMessage(), "\n", 2)
318+
if len(l) > 0 {
319+
subjects = append(subjects, l[0])
301320
}
302-
303-
commits = append(commits, commit)
304321
}
305322

306-
return hashes, commits
323+
return subjects, nil
307324
}
308325

309-
var ErrReachedMergeBase = errors.New("reached Merge Base")
310-
311-
func getCommitSubjects(repo *git.Repository, repoEnv *gitEnv) ([]string, error) {
312-
hashes, commits := hashesFromRefs(repo, repoEnv)
313-
314-
if len(commits) == 1 { // just the last commit
315-
return []string{strings.Split(commits[0].Message, "\n")[0]}, nil
326+
func gitGitlabCommitSubjects(url string, token string, project string, mr string) ([]string, error) {
327+
gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(url))
328+
if err != nil {
329+
log.Fatalf("Failed to create gitlab client: %v", err)
316330
}
317331

318-
mergeBase, err := commits[0].MergeBase(commits[1])
332+
mrID, err := strconv.Atoi(mr)
319333
if err != nil {
320-
log.Fatalf("repo history error %s", err)
334+
return nil, fmt.Errorf("invalid merge request id %s", mr)
321335
}
322-
323-
logOptions := new(git.LogOptions)
324-
logOptions.From = *hashes[0]
325-
logOptions.Order = git.LogOrderCommitterTime
326-
327-
cIter, err := repo.Log(logOptions)
336+
commits, _, err := gitlabClient.MergeRequests.GetMergeRequestCommits(project, mrID, &gitlab.GetMergeRequestCommitsOptions{})
328337
if err != nil {
329-
log.Fatalf("error getting commit log %s", err)
338+
return nil, fmt.Errorf("error fetching commits: %w", err)
330339
}
331340

332-
var subjects []string
333-
334-
gitlabMergeRegex := regexp.MustCompile(`Merge \w{40} into \w{40}`)
335-
336-
err = cIter.ForEach(func(c *object.Commit) error {
337-
if c.Hash == mergeBase[0].Hash {
338-
return ErrReachedMergeBase
339-
}
340-
subjectOnly := strings.Split(c.Message, "\n")[0]
341-
342-
if !(repoEnv.EnvName == GITHUB && repoEnv.Event == "pull_request" && gitlabMergeRegex.Match([]byte(c.Message))) {
343-
// ignore github pull request commits with subject "Merge x into y", these get added automatically by github
344-
subjects = append(subjects, subjectOnly)
345-
log.Printf("collected commit hash %s, subject '%s'", c.Hash, subjectOnly)
346-
} else {
347-
log.Printf("ignoring a pull_request Merge commit hash, %s subject '%s'", c.Hash, subjectOnly)
341+
subjects := []string{}
342+
for _, c := range commits {
343+
l := strings.SplitN(c.Message, "\n", 2)
344+
if len(l) > 0 {
345+
subjects = append(subjects, l[0])
348346
}
349-
350-
return nil
351-
})
352-
if !errors.Is(err, ErrReachedMergeBase) {
353-
return []string{}, fmt.Errorf("error tracing commit history: %w", err)
354347
}
355348

356349
return subjects, nil
357350
}
358351

352+
func getCommitSubjects(repoEnv *gitEnv) ([]string, error) {
353+
if repoEnv.EnvName == GITHUB {
354+
return getGithubCommitSubjects(repoEnv.Token, repoEnv.ProjectID, repoEnv.PMRequestID)
355+
} else if repoEnv.EnvName == GITLAB {
356+
return gitGitlabCommitSubjects(repoEnv.URL, repoEnv.Token, repoEnv.ProjectID, repoEnv.PMRequestID)
357+
}
358+
return nil, fmt.Errorf("unrecognized git environment %s", repoEnv.EnvName)
359+
}
360+
359361
var ErrSubjectList = errors.New("subjects contain errors")
360362

361363
func (c CommitPolicyConfig) CheckSubjectList(subjects []string) error {
@@ -402,12 +404,7 @@ func main() {
402404
log.Fatalf("couldn't auto-detect running environment, please set GITHUB_REF and GITHUB_BASE_REF manually: %s", err)
403405
}
404406

405-
repo, err := git.PlainOpen(repoPath)
406-
if err != nil {
407-
log.Fatalf("couldn't open git local git repo: %s", err)
408-
}
409-
410-
subjects, err := getCommitSubjects(repo, gitEnv)
407+
subjects, err := getCommitSubjects(gitEnv)
411408
if err != nil {
412409
log.Fatalf("error getting commit subjects: %s", err)
413410
}

check-commit/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module check-commit
33
go 1.15
44

55
require (
6-
github.com/go-git/go-git/v5 v5.2.0
6+
github.com/google/go-github/v35 v35.0.0
7+
github.com/xanzy/go-gitlab v0.48.0
8+
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78
79
gopkg.in/yaml.v2 v2.4.0
810
)

0 commit comments

Comments
 (0)