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

thumbrise / commitlint-scope / 26552896453

28 May 2026 03:29AM UTC coverage: 60.755% (-2.1%) from 62.805%
26552896453

push

github

thumbrise
fix: Replace panic with typed error in outsider finder constructor

19 of 34 new or added lines in 3 files covered. (55.88%)

8 existing lines in 2 files now uncovered.

161 of 265 relevant lines covered (60.75%)

0.69 hits per line

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

74.03
/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
        ErrShaLength       = errors.New("sha length must be greater than 0")
14
)
15

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

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

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

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

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

63
        if outsiderFinder == nil {
1✔
NEW
64
                var err error
×
NEW
65

×
NEW
66
                outsiderFinder, err = NewDefaultOutsiderFinder(cfg.Patterns)
×
NEW
67
                if err != nil {
×
NEW
68
                        return nil, err
×
NEW
69
                }
×
70
        }
71

72
        if scopeParser == nil {
1✔
73
                scopeParser = NewDefaultScopeParser(cfg.ScopeRegex)
×
UNCOV
74
        }
×
75

76
        if shaLength == 0 {
1✔
77
                shaLength = 7
×
78
        }
×
79

80
        if shaLength < 0 {
1✔
NEW
81
                return nil, fmt.Errorf("%w, got %d", ErrShaLength, shaLength)
×
UNCOV
82
        }
×
83

84
        return &Validator{
1✔
85
                logger:         logger,
1✔
86
                git:            git,
1✔
87
                outsiderFinder: outsiderFinder,
1✔
88
                scopeParser:    scopeParser,
1✔
89
                shaLength:      shaLength,
1✔
90
        }, nil
1✔
91
}
92

93
func (v *Validator) Validate(ctx context.Context, from, to string) ([]Violation, error) {
1✔
94
        shas, err := v.git.SHA(ctx, from, to)
1✔
95
        if err != nil {
1✔
96
                return nil, fmt.Errorf("git sha: %w", err)
×
97
        }
×
98

99
        var violations []Violation
1✔
100

1✔
101
        for _, sha := range shas {
2✔
102
                message, err := v.git.Message(ctx, sha)
1✔
103
                if err != nil {
2✔
104
                        return nil, fmt.Errorf("%w sha=%s: %w", ErrGetMessage, sha, err)
1✔
105
                }
1✔
106

107
                if message == "" {
2✔
108
                        v.logger.Info("no message, skip", "sha", sha)
1✔
109

1✔
110
                        continue
1✔
111
                }
112

113
                scope := v.scopeParser.Parse(message)
1✔
114
                if scope == "" {
2✔
115
                        v.logger.Info("no scope, skip", "sha", sha, "message", message)
1✔
116

1✔
117
                        continue
1✔
118
                }
119

120
                files, err := v.git.FilesChanged(ctx, sha)
1✔
121
                if err != nil {
1✔
122
                        return nil, fmt.Errorf("%w sha=%s, commit=%s: %w", ErrGetChangedFiles, sha, message, err)
×
123
                }
×
124

125
                if len(files) == 0 {
2✔
126
                        v.logger.Info("no files changed, skip", "sha", sha)
1✔
127

1✔
128
                        continue
1✔
129
                }
130

131
                outsiders := v.outsiderFinder.Find(scope, files)
1✔
132
                if len(outsiders) > 0 {
2✔
133
                        truncatedSHA := sha
1✔
134
                        if len(truncatedSHA) > v.shaLength {
2✔
135
                                truncatedSHA = truncatedSHA[:v.shaLength]
1✔
136
                        }
1✔
137

138
                        violations = append(violations, Violation{
1✔
139
                                SHA:       truncatedSHA,
1✔
140
                                Header:    message,
1✔
141
                                Outsiders: outsiders,
1✔
142
                        })
1✔
143
                }
144
        }
145

146
        return violations, nil
1✔
147
}
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