< Summary - go-semantic-release Coverage

Line coverage
100%
Covered lines: 3
Uncovered lines: 0
Coverable lines: 3
Total lines: 146
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
NewConventionalLinter0%00100%

File(s)

/home/runner/work/go-semantic-release/go-semantic-release/internal/adapters/lint/conventional.go

#LineLine coverage
 1package lint
 2
 3import (
 4  "fmt"
 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.CommitLinter = (*ConventionalLinter)(nil)
 13
 14// ConventionalLinter validates commits against conventional commit rules.
 15type ConventionalLinter struct {
 16  config domain.LintConfig
 17}
 18
 19// NewConventionalLinter creates a new conventional commit linter.
 1020func NewConventionalLinter(cfg domain.LintConfig) *ConventionalLinter {
 1021  return &ConventionalLinter{config: cfg}
 1022}
 23
 24// Lint checks a single commit against all configured rules.
 25func (l *ConventionalLinter) Lint(commit domain.Commit) []domain.LintViolation {
 26  violations := make([]domain.LintViolation, 0, 5)
 27
 28  violations = append(violations, l.checkType(commit)...)
 29  violations = append(violations, l.checkScope(commit)...)
 30  violations = append(violations, l.checkDescription(commit)...)
 31  violations = append(violations, l.checkSubjectLength(commit)...)
 32  violations = append(violations, l.checkBody(commit)...)
 33
 34  return violations
 35}
 36
 37func (l *ConventionalLinter) checkType(commit domain.Commit) []domain.LintViolation {
 38  if commit.Type == "" {
 39    return []domain.LintViolation{{
 40      Rule:     "type-empty",
 41      Message:  "commit message must have a type (e.g., feat, fix)",
 42      Severity: domain.LintError,
 43    }}
 44  }
 45
 46  if len(l.config.AllowedTypes) > 0 {
 47    allowed := false
 48    for _, t := range l.config.AllowedTypes {
 49      if commit.Type == t {
 50        allowed = true
 51        break
 52      }
 53    }
 54    if !allowed {
 55      return []domain.LintViolation{{
 56        Rule:     "type-enum",
 57        Message:  fmt.Sprintf("type %q is not allowed; allowed types: %s", commit.Type, strings.Join(l.config.AllowedTyp
 58        Severity: domain.LintError,
 59      }}
 60    }
 61  }
 62
 63  return nil
 64}
 65
 66func (l *ConventionalLinter) checkScope(commit domain.Commit) []domain.LintViolation {
 67  if l.config.RequireScope && commit.Scope == "" {
 68    return []domain.LintViolation{{
 69      Rule:     "scope-empty",
 70      Message:  "commit message must have a scope",
 71      Severity: domain.LintError,
 72    }}
 73  }
 74
 75  if commit.Scope != "" && len(l.config.AllowedScopes) > 0 {
 76    allowed := false
 77    for _, s := range l.config.AllowedScopes {
 78      if commit.Scope == s {
 79        allowed = true
 80        break
 81      }
 82    }
 83    if !allowed {
 84      return []domain.LintViolation{{
 85        Rule:     "scope-enum",
 86        Message:  fmt.Sprintf("scope %q is not allowed; allowed scopes: %s", commit.Scope, strings.Join(l.config.Allowed
 87        Severity: domain.LintError,
 88      }}
 89    }
 90  }
 91
 92  return nil
 93}
 94
 95func (l *ConventionalLinter) checkDescription(commit domain.Commit) []domain.LintViolation {
 96  if commit.Description == "" {
 97    return []domain.LintViolation{{
 98      Rule:     "description-empty",
 99      Message:  "commit message must have a description",
 100      Severity: domain.LintError,
 101    }}
 102  }
 103
 104  if strings.HasSuffix(commit.Description, ".") {
 105    return []domain.LintViolation{{
 106      Rule:     "description-trailing-period",
 107      Message:  "description must not end with a period",
 108      Severity: domain.LintWarning,
 109    }}
 110  }
 111
 112  return nil
 113}
 114
 115func (l *ConventionalLinter) checkSubjectLength(commit domain.Commit) []domain.LintViolation {
 116  if l.config.MaxSubjectLength <= 0 {
 117    return nil
 118  }
 119
 120  subject := commit.Message
 121  if idx := strings.IndexByte(subject, '\n'); idx >= 0 {
 122    subject = subject[:idx]
 123  }
 124
 125  if len(subject) > l.config.MaxSubjectLength {
 126    return []domain.LintViolation{{
 127      Rule:     "subject-max-length",
 128      Message:  fmt.Sprintf("subject line is %d characters, maximum is %d", len(subject), l.config.MaxSubjectLength),
 129      Severity: domain.LintWarning,
 130    }}
 131  }
 132
 133  return nil
 134}
 135
 136func (l *ConventionalLinter) checkBody(commit domain.Commit) []domain.LintViolation {
 137  if l.config.RequireBody && commit.Body == "" {
 138    return []domain.LintViolation{{
 139      Rule:     "body-empty",
 140      Message:  "commit message must have a body",
 141      Severity: domain.LintWarning,
 142    }}
 143  }
 144
 145  return nil
 146}

Methods/Properties

NewConventionalLinter