Skip to content
Merged
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
1 change: 1 addition & 0 deletions .aspell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ allowed:
- url
- english
- lang
- junit
5 changes: 4 additions & 1 deletion aspell/aspell.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sort"
"strings"

"github.com/haproxytech/check-commit/v5/junit"
"github.com/haproxytech/check-commit/v5/match"

"github.com/fatih/camelcase"
Expand Down Expand Up @@ -121,7 +122,7 @@ func (a Aspell) checkSingle(data string, allowedWords []string) error {
return nil
}

func (a Aspell) Check(subjects []string, commitsFull []string, content []map[string]string) error {
func (a Aspell) Check(subjects []string, commitsFull []string, content []map[string]string, junitSuite junit.Interface) error {
var commitsFullData []string
for _, c := range commitsFull {
commit := []string{}
Expand Down Expand Up @@ -171,6 +172,7 @@ func (a Aspell) Check(subjects []string, commitsFull []string, content []map[str
imports = match.GetImportWordsFromGoFile(name)
}
if err := a.checkSingle(v, imports); err != nil {
junitSuite.AddMessageFailed(name, "aspell check failed", err.Error())
log.Println(name, err.Error())
response += fmt.Sprintf("%s\n", err)
}
Expand All @@ -183,6 +185,7 @@ func (a Aspell) Check(subjects []string, commitsFull []string, content []map[str

for _, subject := range checks {
if err := a.checkSingle(subject, []string{}); err != nil {
junitSuite.AddMessageFailed("commit message", "aspell check failed", err.Error())
log.Println("commit message", err.Error())
response += fmt.Sprintf("%s\n", err)
}
Expand Down
4 changes: 3 additions & 1 deletion aspell/aspell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package aspell

import (
"testing"

"github.com/haproxytech/check-commit/v5/junit"
)

func Test_checkWithAspell(t *testing.T) {
Expand Down Expand Up @@ -107,7 +109,7 @@ func TestAspell_Check(t *testing.T) {
AllowedWords: tt.fields.AllowedWords,
HelpText: tt.fields.HelpText,
}
if err := a.Check(tt.args.subjects, tt.args.commitsFull, tt.args.content); (err != nil) != tt.wantErr {
if err := a.Check(tt.args.subjects, tt.args.commitsFull, tt.args.content, &junit.JunitSuiteDummy{}); (err != nil) != tt.wantErr {
t.Errorf("Aspell.Check() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
3 changes: 2 additions & 1 deletion aspell_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/haproxytech/check-commit/v5/aspell"
"github.com/haproxytech/check-commit/v5/junit"
)

func Test_Aspell(t *testing.T) {
Expand Down Expand Up @@ -35,7 +36,7 @@ func Test_Aspell(t *testing.T) {
}
err = aspell.Check([]string{"subject"}, []string{"body"}, []map[string]string{
{filename: readme},
})
}, &junit.JunitSuiteDummy{})
if err != nil {
t.Errorf("checkWithAspell() error = %v", err)
}
Expand Down
64 changes: 49 additions & 15 deletions check.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"unicode/utf8"

"github.com/google/go-github/v56/github"
"github.com/haproxytech/check-commit/v5/junit"
gitlab "gitlab.com/gitlab-org/api/client-go"

git "github.com/go-git/go-git/v5"
Expand Down Expand Up @@ -90,24 +91,33 @@ TagOrder:

var ErrSubjectMessageFormat = errors.New("invalid subject message format")

func checkSubjectText(subject string) error {
func checkSubjectText(subject string, junitSuite junit.Interface) error {
subjectLen := utf8.RuneCountInString(subject)
subjectParts := strings.Fields(subject)
subjectPartsLen := len(subjectParts)

if subject != strings.Join(subjectParts, " ") {
junitSuite.AddMessageFailed(ErrSubjectMessageFormat.Error(), "malformatted subject string (trailing or double spaces?)", fmt.Sprintf("subject: %s", subject))
return fmt.Errorf(
"malformatted subject string (trailing or double spaces?): '%s' (%w)",
subject, ErrSubjectMessageFormat)
}

if subjectPartsLen < MINSUBJECTPARTS || subjectPartsLen > MAXSUBJECTPARTS {
junitSuite.AddMessageFailed(
ErrSubjectMessageFormat.Error(),
fmt.Sprintf("subject word count out of bounds [words %d < %d < %d]", MINSUBJECTPARTS, subjectPartsLen, MAXSUBJECTPARTS),
fmt.Sprintf("subject: %s", subject))
return fmt.Errorf(
"subject word count out of bounds [words %d < %d < %d] '%s': %w",
MINSUBJECTPARTS, subjectPartsLen, MAXSUBJECTPARTS, subjectParts, ErrSubjectMessageFormat)
}

if subjectLen < MINSUBJECTLEN || subjectLen > MAXSUBJECTLEN {
junitSuite.AddMessageFailed(
ErrSubjectMessageFormat.Error(),
fmt.Sprintf("subject length out of bounds [len %d < %d < %d]", MINSUBJECTLEN, subjectLen, MAXSUBJECTLEN),
fmt.Sprintf("subject: %s", subject))
return fmt.Errorf(
"subject length out of bounds [len %d < %d < %d] '%s': %w",
MINSUBJECTLEN, subjectLen, MAXSUBJECTLEN, subject, ErrSubjectMessageFormat)
Expand All @@ -128,6 +138,7 @@ func (c CommitPolicyConfig) CheckPatchTypes(tag, severity string, patchTypeName
}

if c.PatchTypes[patchTypeName].Scope == "" {

log.Printf("unable to verify severity %s without definitions", severity)

break // subject has severity but there is no definition to verify it
Expand All @@ -148,10 +159,11 @@ func (c CommitPolicyConfig) CheckPatchTypes(tag, severity string, patchTypeName

var ErrTagScope = errors.New("invalid tag and or severity")

func (c CommitPolicyConfig) CheckSubject(rawSubject []byte) error {
func (c CommitPolicyConfig) CheckSubject(rawSubject []byte, junitSuite junit.Interface) error {
// check for ascii-only before anything else
for i := 0; i < len(rawSubject); i++ {
if rawSubject[i] > unicode.MaxASCII {
junitSuite.AddMessageFailed("", "non-ascii characters detected in commit subject", fmt.Sprintf("subject: %s", rawSubject))
log.Printf("non-ascii characters detected in in subject:\n%s", hex.Dump(rawSubject))

return fmt.Errorf("non-ascii characters in commit subject: %w", ErrTagScope)
Expand All @@ -174,6 +186,7 @@ func (c CommitPolicyConfig) CheckSubject(rawSubject []byte) error {
submatch := r.FindSubmatchIndex(rawSubject)
if len(submatch) == 0 { // no match
if !tagOK {
junitSuite.AddMessageFailed("", "invalid or missing tag/severity in commit message", fmt.Sprintf("subject: %s", rawSubject))
log.Printf("unable to find match in %s\n", rawSubject)

return fmt.Errorf("invalid tag or no tag found, searched through [%s]: %w",
Expand All @@ -199,6 +212,7 @@ func (c CommitPolicyConfig) CheckSubject(rawSubject []byte) error {
candidates = append(candidates, string(tagPart))

if !tagOK {
junitSuite.AddMessageFailed("", "invalid tag/severity in commit message", fmt.Sprintf("subject: %s", rawSubject))
log.Printf("unable to find match in %s\n", candidates)

return fmt.Errorf("invalid tag or no tag found, searched through [%s]: %w",
Expand All @@ -208,10 +222,11 @@ func (c CommitPolicyConfig) CheckSubject(rawSubject []byte) error {

submatch := r.FindSubmatchIndex(rawSubject)
if len(submatch) != 0 { // no match
junitSuite.AddMessageFailed("", "unprocessed tags detected in commit message", fmt.Sprintf("subject: %s", rawSubject))
return fmt.Errorf("detected unprocessed tags, %w", ErrTagScope)
}

return checkSubjectText(string(rawSubject))
return checkSubjectText(string(rawSubject), junitSuite)
}

func (c CommitPolicyConfig) IsEmpty() bool {
Expand Down Expand Up @@ -268,7 +283,7 @@ func LoadCommitPolicy(filename string) (CommitPolicyConfig, error) {
return commitPolicy, nil
}

func getGithubCommitData() ([]string, []string, []map[string]string, error) {
func getGithubCommitData(junitSuite junit.Interface) ([]string, []string, []map[string]string, error) {
token := os.Getenv("API_TOKEN")
repo := os.Getenv("GITHUB_REPOSITORY")
ref := os.Getenv("GITHUB_REF")
Expand All @@ -285,22 +300,26 @@ func getGithubCommitData() ([]string, []string, []map[string]string, error) {
if event == "pull_request" {
repoSlice := strings.SplitN(repo, "/", 2)
if len(repoSlice) < 2 {
junitSuite.AddMessageFailed("", "error fetching owner and project from repo", fmt.Sprintf("invalid repository format: %s", repo))
return nil, nil, nil, fmt.Errorf("error fetching owner and project from repo %s", repo)
}
owner := repoSlice[0]
project := repoSlice[1]

refSlice := strings.SplitN(ref, "/", 4)
if len(refSlice) < 3 {
return nil, nil, nil, fmt.Errorf("error fetching pr from ref %s", ref)
junitSuite.AddMessageFailed("", "error fetching PR number from ref", fmt.Sprintf("invalid ref format: %s", ref))
return nil, nil, nil, fmt.Errorf("error fetching PR from ref %s", ref)
}
prNo, err := strconv.Atoi(refSlice[2])
if err != nil {
return nil, nil, nil, fmt.Errorf("Error fetching pr number from %s: %w", refSlice[2], err)
junitSuite.AddMessageFailed("", "error fetching PR number from ref", fmt.Sprintf("invalid pr number: %s", refSlice[2]))
return nil, nil, nil, fmt.Errorf("Error fetching PR number from %s: %w", refSlice[2], err)
}

commits, _, err := githubClient.PullRequests.ListCommits(ctx, owner, project, prNo, &github.ListOptions{})
if err != nil {
junitSuite.AddMessageFailed("", "error fetching commits", err.Error())
return nil, nil, nil, fmt.Errorf("error fetching commits: %w", err)
}

Expand All @@ -315,6 +334,7 @@ func getGithubCommitData() ([]string, []string, []map[string]string, error) {
}
if len(l) > 1 {
if l[1] != "" {
junitSuite.AddMessageFailed("", "empty line between subject and body is required", fmt.Sprintf("%s %s", hash, l[0]))
return nil, nil, nil, fmt.Errorf("empty line between subject and body is required: %s %s", hash, l[0])
}
}
Expand All @@ -326,6 +346,7 @@ func getGithubCommitData() ([]string, []string, []map[string]string, error) {

files, _, err := githubClient.PullRequests.ListFiles(ctx, owner, project, prNo, &github.ListOptions{})
if err != nil {
junitSuite.AddMessageFailed("", "error fetching files", err.Error())
return nil, nil, nil, fmt.Errorf("error fetching files: %w", err)
}
content := map[string]string{}
Expand All @@ -339,20 +360,23 @@ func getGithubCommitData() ([]string, []string, []map[string]string, error) {
}
return subjects, messages, diffs, nil
} else {
junitSuite.AddMessageFailed("", "unsupported event name", fmt.Sprintf("unsupported event name: %s", event))
return nil, nil, nil, fmt.Errorf("unsupported event name: %s", event)
}
}

func getLocalCommitData() ([]string, []string, []map[string]string, error) {
func getLocalCommitData(junitSuite junit.Interface) ([]string, []string, []map[string]string, error) {
repo, err := git.PlainOpen(".")
if err != nil {
junitSuite.AddMessageFailed("", "error opening local git repository", err.Error())
return nil, nil, nil, err
}

iter, err := repo.Log(&git.LogOptions{
Order: git.LogOrderCommitterTime,
})
if err != nil {
junitSuite.AddMessageFailed("", "error getting git log iterator", err.Error())
return nil, nil, nil, err
}

Expand All @@ -372,6 +396,7 @@ func getLocalCommitData() ([]string, []string, []map[string]string, error) {
break
}
if err != nil {
junitSuite.AddMessageFailed("", "error iterating through git commits", err.Error())
return nil, nil, nil, err
}
if committer == "" {
Expand All @@ -392,6 +417,7 @@ func getLocalCommitData() ([]string, []string, []map[string]string, error) {
}
if len(l) > 1 {
if l[1] != "" {
junitSuite.AddMessageFailed("", "empty line between subject and body is required", fmt.Sprintf("%s %s", commitHash, l[0]))
return nil, nil, nil, fmt.Errorf("empty line between subject and body is required: %s %s", commitHash, l[0])
}
}
Expand All @@ -409,13 +435,15 @@ func getLocalCommitData() ([]string, []string, []map[string]string, error) {
tree2, _ := commit2.Tree()
changes, err := object.DiffTree(tree2, tree1)
if err != nil {
junitSuite.AddMessageFailed("", "error getting git commit changes", err.Error())
return nil, nil, nil, err
}

// Print the list of changed files and their content (patch)
for _, change := range changes {
patch, err := change.Patch()
if err != nil {
junitSuite.AddMessageFailed("", "error getting git patch", err.Error())
return nil, nil, nil, err
}
for _, file := range patch.FilePatches() {
Expand Down Expand Up @@ -455,28 +483,32 @@ func cleanGitPatch(patch string) string {
return patch
}

func getGitlabCommitData() ([]string, []string, []map[string]string, error) {
func getGitlabCommitData(junitSuite junit.Interface) ([]string, []string, []map[string]string, error) {
gitlab_url := os.Getenv("CI_API_V4_URL")
token := os.Getenv("API_TOKEN")
mri := os.Getenv("CI_MERGE_REQUEST_IID")
project := os.Getenv("CI_MERGE_REQUEST_PROJECT_ID")

gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(gitlab_url))
if err != nil {
log.Fatalf("Failed to create gitlab client: %v", err)
junitSuite.AddMessageFailed("", "failed to create gitlab client", err.Error())
return nil, nil, nil, fmt.Errorf("failed to create gitlab client: %w", err)
}

mrIID, err := strconv.Atoi(mri)
if err != nil {
junitSuite.AddMessageFailed("", "invalid merge request id", err.Error())
return nil, nil, nil, fmt.Errorf("invalid merge request id %s", mri)
}

projectID, err := strconv.Atoi(project)
if err != nil {
junitSuite.AddMessageFailed("", "invalid project id", err.Error())
return nil, nil, nil, fmt.Errorf("invalid project id %s", project)
}
commits, _, err := gitlabClient.MergeRequests.GetMergeRequestCommits(projectID, mrIID, &gitlab.GetMergeRequestCommitsOptions{})
if err != nil {
junitSuite.AddMessageFailed("", "error fetching commits", err.Error())
return nil, nil, nil, fmt.Errorf("error fetching commits: %w", err)
}

Expand All @@ -489,6 +521,7 @@ func getGitlabCommitData() ([]string, []string, []map[string]string, error) {
if len(l) > 0 {
if len(l) > 1 {
if l[1] != "" {
junitSuite.AddMessageFailed("", "empty line between subject and body is required", fmt.Sprintf("%s %s", hash, l[0]))
return nil, nil, nil, fmt.Errorf("empty line between subject and body is required: %s %s", hash, l[0])
}
}
Expand All @@ -497,6 +530,7 @@ func getGitlabCommitData() ([]string, []string, []map[string]string, error) {
messages = append(messages, c.Message)
diff, _, err := gitlabClient.MergeRequests.ListMergeRequestDiffs(projectID, mrIID, &gitlab.ListMergeRequestDiffsOptions{})
if err != nil {
junitSuite.AddMessageFailed("", "error fetching commit changes", err.Error())
return nil, nil, nil, fmt.Errorf("error fetching commit changes: %w", err)
}
content := map[string]string{}
Expand All @@ -513,25 +547,25 @@ func getGitlabCommitData() ([]string, []string, []map[string]string, error) {
return subjects, messages, diffs, nil
}

func getCommitData(repoEnv string) ([]string, []string, []map[string]string, error) {
func getCommitData(repoEnv string, junitSuite junit.Interface) ([]string, []string, []map[string]string, error) {
if repoEnv == GITHUB {
return getGithubCommitData()
return getGithubCommitData(junitSuite)
} else if repoEnv == GITLAB {
return getGitlabCommitData()
return getGitlabCommitData(junitSuite)
} else if repoEnv == LOCAL {
return getLocalCommitData()
return getLocalCommitData(junitSuite)
}
return nil, nil, nil, fmt.Errorf("unrecognized git environment %s", repoEnv)
}

var ErrSubjectList = errors.New("subjects contain errors")

func (c CommitPolicyConfig) CheckSubjectList(subjects []string) error {
func (c CommitPolicyConfig) CheckSubjectList(subjects []string, junitSuite junit.Interface) error {
errors := false

for _, subject := range subjects {
subject = strings.Trim(subject, "'")
if err := c.CheckSubject([]byte(subject)); err != nil {
if err := c.CheckSubject([]byte(subject), junitSuite); err != nil {
log.Printf("%s, original subject message '%s'", err, subject)

errors = true
Expand Down
3 changes: 2 additions & 1 deletion check_different_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"testing"

"github.com/haproxytech/check-commit/v5/junit"
yaml "gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -100,7 +101,7 @@ func TestDifferentPolicy(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if err := c.CheckSubject([]byte(tt.subject)); (err != nil) != tt.wantErr {
if err := c.CheckSubject([]byte(tt.subject), &junit.JunitSuiteDummy{}); (err != nil) != tt.wantErr {
t.Errorf("checkSubject() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
4 changes: 3 additions & 1 deletion check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package main

import (
"testing"

"github.com/haproxytech/check-commit/v5/junit"
)

func TestCheckSubject(t *testing.T) {
Expand All @@ -13,7 +15,7 @@ func TestCheckSubject(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
if err := c.CheckSubject([]byte(tt.subject)); (err != nil) != tt.wantErr {
if err := c.CheckSubject([]byte(tt.subject), &junit.JunitSuiteDummy{}); (err != nil) != tt.wantErr {
t.Errorf("checkSubject() error = %v, wantErr %v", err, tt.wantErr)
}
})
Expand Down
Loading