< Summary - go-semantic-release Coverage

Information
Line coverage
100%
Covered lines: 28
Uncovered lines: 0
Coverable lines: 28
Total lines: 112
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Parse0%00100%

File(s)

/home/runner/work/go-semantic-release/go-semantic-release/internal/adapters/git/commit_parser.go

#LineLine coverage
 1package git
 2
 3import (
 4  "regexp"
 5  "strings"
 6
 7  "github.com/jedi-knights/go-semantic-release/internal/domain"
 8  "github.com/jedi-knights/go-semantic-release/internal/ports"
 9)
 10
 11// Compile-time interface compliance check.
 12var _ ports.CommitParser = (*ConventionalCommitParser)(nil)
 13
 14var conventionalCommitRe = regexp.MustCompile(
 15  `^(?P<type>\w+)` +
 16    `(?:\((?P<scope>[^)]*)\))?` +
 17    `(?P<breaking>!)?` +
 18    `:\s*(?P<description>.+)$`,
 19)
 20
 21// ConventionalCommitParser implements ports.CommitParser for Conventional Commits.
 22type ConventionalCommitParser struct{}
 23
 24// NewConventionalCommitParser creates a new parser.
 25func NewConventionalCommitParser() *ConventionalCommitParser {
 26  return &ConventionalCommitParser{}
 27}
 28
 1129func (p *ConventionalCommitParser) Parse(message string) (domain.Commit, error) {
 1130  lines := strings.SplitN(message, "\n", 2)
 1131  subject := strings.TrimSpace(lines[0])
 1132
 1133  matches := conventionalCommitRe.FindStringSubmatch(subject)
 134  if matches == nil {
 135    return domain.Commit{
 136      Message:     subject,
 137      Description: subject,
 138    }, nil
 139  }
 40
 1041  commit := domain.Commit{
 1042    Message:     subject,
 1043    Type:        matches[1],
 1044    Scope:       matches[2],
 1045    Description: matches[4],
 1046  }
 1047
 1048  // Check for ! marker.
 249  if matches[3] == "!" {
 250    commit.IsBreakingChange = true
 251  }
 52
 53  // Parse body and footer.
 454  if len(lines) > 1 {
 455    body := strings.TrimSpace(lines[1])
 456    commit.Body, commit.Footer = splitBodyFooter(body)
 457    detectBreakingChange(&commit)
 458  }
 59
 1060  return commit, nil
 61}
 62
 63func splitBodyFooter(text string) (body, footer string) {
 64  // Footer is separated from body by a blank line and starts with a token.
 65  parts := strings.Split(text, "\n\n")
 66  if len(parts) <= 1 {
 67    return text, ""
 68  }
 69
 70  lastPart := parts[len(parts)-1]
 71  if isFooter(lastPart) {
 72    return strings.Join(parts[:len(parts)-1], "\n\n"), lastPart
 73  }
 74  return text, ""
 75}
 76
 77var footerTokenRe = regexp.MustCompile(`^[\w-]+(?:: | #)`)
 78
 79func isFooter(text string) bool {
 80  lines := strings.Split(text, "\n")
 81  if len(lines) == 0 {
 82    return false
 83  }
 84  return footerTokenRe.MatchString(lines[0]) ||
 85    strings.HasPrefix(lines[0], "BREAKING CHANGE:") ||
 86    strings.HasPrefix(lines[0], "BREAKING-CHANGE:")
 87}
 88
 89func detectBreakingChange(commit *domain.Commit) {
 90  for _, prefix := range []string{"BREAKING CHANGE:", "BREAKING-CHANGE:"} {
 91    if note := findBreakingNote(commit.Footer, prefix); note != "" {
 92      commit.IsBreakingChange = true
 93      commit.BreakingNote = note
 94      return
 95    }
 96    if note := findBreakingNote(commit.Body, prefix); note != "" {
 97      commit.IsBreakingChange = true
 98      commit.BreakingNote = note
 99      return
 100    }
 101  }
 102}
 103
 104func findBreakingNote(text, prefix string) string {
 105  for _, line := range strings.Split(text, "\n") {
 106    line = strings.TrimSpace(line)
 107    if strings.HasPrefix(line, prefix) {
 108      return strings.TrimSpace(strings.TrimPrefix(line, prefix))
 109    }
 110  }
 111  return ""
 112}

Methods/Properties

Parse