• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thumbrise / commitlint-scope / 26543706456

27 May 2026 10:58PM UTC coverage: 62.805% (+17.4%) from 45.455%
26543706456

push

github

thumbrise
feat: Validator options, default git integration

- Replace validator args to options struct
- Move validator interfaces to validator.go
- Add default implementations if options fields not set

Need to implement OutsiderFinder, ScopeParser

9 of 21 new or added lines in 2 files covered. (42.86%)

11 existing lines in 2 files now uncovered.

103 of 164 relevant lines covered (62.8%)

0.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

81.43
/pkg/validator/validator.go
1
package validator
2

3
import (
4
        "context"
5
        "errors"
6
        "fmt"
7
        "log/slog"
8
)
9

10
var (
11
        ErrGetMessage      = errors.New("get commit message")
12
        ErrGetChangedFiles = errors.New("get changed files")
13
)
14

15
type Violation struct {
16
        SHA       string   `json:"sha"`
17
        Header    string   `json:"header"`
18
        Outsiders []string `json:"outsiders"`
19
}
20
type Git interface {
21
        SHA(ctx context.Context, from, to string) ([]string, error)
22
        Message(ctx context.Context, sha string) (string, error)
23
        FilesChanged(ctx context.Context, sha string) ([]string, error)
24
}
25
type ScopeParser interface {
26
        Parse(message string) (string, bool) // scope, ok
27
}
28
type OutsiderFinder interface {
29
        Find(scope string, files []string) []string
30
}
31

32
type Options struct {
33
        Logger         *slog.Logger
34
        SHALength      int
35
        Git            Git
36
        OutsiderFinder OutsiderFinder
37
        ScopeParser    ScopeParser
38
}
39
type Validator struct {
40
        logger         *slog.Logger
41
        git            Git
42
        outsiderFinder OutsiderFinder
43
        scopeParser    ScopeParser
44
        shaLength      int
45
}
46

47
func NewValidator(options Options) *Validator {
1✔
48
        logger := options.Logger
1✔
49
        shaLength := options.SHALength
1✔
50
        scopeParser := options.ScopeParser
1✔
51
        outsiderFinder := options.OutsiderFinder
1✔
52
        git := options.Git
1✔
53

1✔
54
        if logger == nil {
1✔
55
                logger = slog.Default()
×
56
        }
×
57

58
        if git == nil {
1✔
NEW
59
                git = NewDefaultGit("")
×
UNCOV
60
        }
×
61

62
        if outsiderFinder == nil {
1✔
NEW
63
                panic("OutsiderFinder must not be nil")
×
64
        }
65

66
        if scopeParser == nil {
1✔
NEW
67
                panic("ScopeParser must not be nil")
×
68
        }
69

70
        if shaLength == 0 {
1✔
NEW
71
                shaLength = 7
×
UNCOV
72
        }
×
73

74
        if shaLength < 0 {
1✔
NEW
75
                panic("ShaLength must be greater than 0")
×
76
        }
77

78
        return &Validator{
1✔
79
                logger:         logger,
1✔
80
                git:            git,
1✔
81
                outsiderFinder: outsiderFinder,
1✔
82
                scopeParser:    scopeParser,
1✔
83
                shaLength:      shaLength,
1✔
84
        }
1✔
85
}
86

87
func (v *Validator) Validate(ctx context.Context, from, to string) ([]Violation, error) {
1✔
88
        shas, err := v.git.SHA(ctx, from, to)
1✔
89
        if err != nil {
1✔
90
                return nil, fmt.Errorf("git sha: %w", err)
×
91
        }
×
92

93
        var violations []Violation
1✔
94

1✔
95
        for _, sha := range shas {
2✔
96
                message, err := v.git.Message(ctx, sha)
1✔
97
                if err != nil {
2✔
98
                        return nil, fmt.Errorf("%w sha=%s: %w", ErrGetMessage, sha, err)
1✔
99
                }
1✔
100

101
                if message == "" {
2✔
102
                        v.logger.Info("no message, skip", "sha", sha)
1✔
103

1✔
104
                        continue
1✔
105
                }
106

107
                scope, ok := v.scopeParser.Parse(message)
1✔
108
                if !ok {
2✔
109
                        v.logger.Info("no scope, skip", "sha", sha, "message", message)
1✔
110

1✔
111
                        continue
1✔
112
                }
113

114
                files, err := v.git.FilesChanged(ctx, sha)
1✔
115
                if err != nil {
1✔
116
                        return nil, fmt.Errorf("%w sha=%s, commit=%s: %w", ErrGetChangedFiles, sha, message, err)
×
117
                }
×
118

119
                if len(files) == 0 {
2✔
120
                        v.logger.Info("no files changed, skip", "sha", sha)
1✔
121

1✔
122
                        continue
1✔
123
                }
124

125
                outsiders := v.outsiderFinder.Find(scope, files)
1✔
126
                if len(outsiders) > 0 {
2✔
127
                        truncatedSHA := sha
1✔
128
                        if len(truncatedSHA) > v.shaLength {
2✔
129
                                truncatedSHA = truncatedSHA[:v.shaLength]
1✔
130
                        }
1✔
131

132
                        violations = append(violations, Violation{
1✔
133
                                SHA:       truncatedSHA,
1✔
134
                                Header:    message,
1✔
135
                                Outsiders: outsiders,
1✔
136
                        })
1✔
137
                }
138
        }
139

140
        return violations, nil
1✔
141
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc