Skip to content

Commit 85f3e5b

Browse files
committed
feat!: support ignores commit messages
1 parent d6fada9 commit 85f3e5b

File tree

11 files changed

+210
-152
lines changed

11 files changed

+210
-152
lines changed

lib/src/is_ignored.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
bool isIgnored(String message,
2+
{bool? defaultIgnores, Iterable<String>? ignores}) {
3+
final base = defaultIgnores == false ? [] : wildcards;
4+
return [...base, ...?ignores?.map(ignore)].any((mathcer) => mathcer(message));
5+
}
6+
7+
final wildcards = [
8+
ignore(
9+
r'((Merge pull request)|(Merge (.*?) into (.*?)|(Merge branch (.*?)))(?:\r?\n)*$)'),
10+
ignore(r'(Merge tag (.*?))(?:\r?\n)*$'),
11+
ignore(r'(R|r)evert (.*)'),
12+
ignore(r'(fixup|squash)!'),
13+
ignore(r'(Merged (.*?)(in|into) (.*)|Merged PR (.*): (.*))'),
14+
ignore(r'Merge remote-tracking branch(\s*)(.*)'),
15+
ignore(r'Automatic merge(.*)'),
16+
ignore(r'Auto-merged (.*?) into (.*)'),
17+
];
18+
19+
Matcher ignore(String pattern) =>
20+
(String message) => RegExp(pattern).hasMatch(message);
21+
22+
typedef Matcher = bool Function(String);

lib/src/lint.dart

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'is_ignored.dart';
12
import 'parse.dart';
23
import 'rules.dart';
34
import 'types/commit.dart';
@@ -7,12 +8,18 @@ import 'types/rule.dart';
78
///
89
/// Lint commit [message] with configured [rules]
910
///
10-
Future<LintOutcome> lint(String message, Map<String, RuleConfig> rules) async {
11-
// Parse the commit message
11+
Future<LintOutcome> lint(String message, Map<String, Rule> rules,
12+
{bool? defaultIgnores, Iterable<String>? ignores}) async {
13+
/// Found a wildcard match, skip
14+
if (isIgnored(message, defaultIgnores: defaultIgnores, ignores: ignores)) {
15+
return LintOutcome(input: message, valid: true, errors: [], warnings: []);
16+
}
17+
18+
/// Parse the commit message
1219
final commit = message.isEmpty ? Commit.empty() : parse(message);
1320

1421
if (commit.header.isEmpty && commit.body == null && commit.footer == null) {
15-
// Commit is empty, skip
22+
/// Commit is empty, skip
1623
return LintOutcome(input: message, valid: true, errors: [], warnings: []);
1724
}
1825
final allRules = Map.of(supportedRules);
@@ -28,7 +35,7 @@ Future<LintOutcome> lint(String message, Map<String, RuleConfig> rules) async {
2835
/// Validate against all rules
2936
final results = rules.entries
3037
// Level 0 rules are ignored
31-
.where((entry) => entry.value.severity != RuleConfigSeverity.ignore)
38+
.where((entry) => entry.value.severity != RuleSeverity.ignore)
3239
.map((entry) {
3340
final name = entry.key;
3441
final config = entry.value;
@@ -49,9 +56,9 @@ Future<LintOutcome> lint(String message, Map<String, RuleConfig> rules) async {
4956
.where((outcome) => !outcome.valid)
5057
.toList();
5158
final errors =
52-
results.where((element) => element.level == RuleConfigSeverity.error);
59+
results.where((element) => element.level == RuleSeverity.error);
5360
final warnings =
54-
results.where((element) => element.level == RuleConfigSeverity.warning);
61+
results.where((element) => element.level == RuleSeverity.warning);
5562
return LintOutcome(
5663
input: message,
5764
valid: errors.isEmpty,

lib/src/load.dart

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,117 +5,117 @@ import 'package:path/path.dart';
55
import 'package:yaml/yaml.dart';
66

77
import 'types/case.dart';
8+
import 'types/commitlint.dart';
89
import 'types/rule.dart';
910

1011
///
11-
/// Load configured rules in given [file] from given [dir].
12+
/// Load configured rules in given [path] from given [directory].
1213
///
13-
Future<Map<String, RuleConfig>> load({
14-
required String file,
15-
String? dir,
14+
Future<CommitLint> load(
15+
String path, {
16+
Directory? directory,
1617
}) async {
17-
Map<String, RuleConfig> rules = {};
18-
Uri? uri;
19-
if (!file.startsWith('package:')) {
20-
uri = toUri(join(dir ?? Directory.current.path, file));
21-
dir = dirname(uri.path);
18+
File? file;
19+
if (!path.startsWith('package:')) {
20+
final uri = toUri(join(directory?.path ?? Directory.current.path, path));
21+
file = File.fromUri(uri);
2222
} else {
23-
uri = await Isolate.resolvePackageUri(Uri.parse(file));
24-
dir = uri?.path.split('/lib/').first;
23+
final uri = await Isolate.resolvePackageUri(Uri.parse(path));
24+
if (uri != null) {
25+
file = File.fromUri(uri);
26+
}
2527
}
26-
if (uri != null) {
27-
final file = File.fromUri(uri);
28-
if (await file.exists()) {
29-
final yaml = loadYaml(await file.readAsString());
30-
final include = yaml?['include'] as String?;
31-
final rulesMap = yaml?['rules'] as Map?;
32-
if (rulesMap != null) {
33-
for (var entry in rulesMap.entries) {
34-
rules[entry.key] = _extractRuleConfig(entry.value);
35-
}
36-
}
37-
if (include != null) {
38-
final upstream = await load(dir: dir, file: include);
39-
if (upstream.isNotEmpty) {
40-
rules = {
41-
...upstream,
42-
...rules,
43-
};
44-
}
28+
if (file != null && file.existsSync()) {
29+
final yaml = loadYaml(await file.readAsString());
30+
final include = yaml?['include'] as String?;
31+
final rulesMap = yaml?['rules'] as Map?;
32+
Map<String, Rule> rules = {};
33+
if (rulesMap != null) {
34+
for (var entry in rulesMap.entries) {
35+
rules[entry.key] = _extractRule(entry.value);
4536
}
4637
}
38+
final config = CommitLint(
39+
rules: rules,
40+
deafultIgnores: yaml?['deafultIgnores'] as bool?,
41+
ignores: yaml?['ignores'] as Iterable<String>?);
42+
if (include != null) {
43+
final upstream = await load(include, directory: file.parent);
44+
return config.inherit(upstream);
45+
}
46+
return config;
4747
}
48-
return rules;
48+
return CommitLint();
4949
}
5050

51-
RuleConfig _extractRuleConfig(dynamic config) {
51+
Rule _extractRule(dynamic config) {
5252
if (config is! List) {
5353
throw Exception('rule config must be list, but get $config');
5454
}
5555
if (config.isEmpty || config.length < 2 || config.length > 3) {
5656
throw Exception(
5757
'rule config must contain at least two, at most three items.');
5858
}
59-
final severity = _extractRuleConfigSeverity(config.first as int);
60-
final condition = _extractRuleConfigCondition(config.elementAt(1) as String);
59+
final severity = _extractRuleSeverity(config.first as int);
60+
final condition = _extractRuleCondition(config.elementAt(1) as String);
6161
dynamic value;
6262
if (config.length == 3) {
6363
value = config.last;
6464
}
6565
if (value == null) {
66-
return RuleConfig(severity: severity, condition: condition);
66+
return Rule(severity: severity, condition: condition);
6767
}
6868
if (value is num) {
69-
return LengthRuleConfig(
69+
return LengthRule(
7070
severity: severity,
7171
condition: condition,
7272
length: value,
7373
);
7474
}
7575
if (value is String) {
7676
if (value.endsWith('-case')) {
77-
return CaseRuleConfig(
77+
return CaseRule(
7878
severity: severity,
7979
condition: condition,
8080
type: _extractCase(value),
8181
);
8282
} else {
83-
return ValueRuleConfig(
83+
return ValueRule(
8484
severity: severity,
8585
condition: condition,
8686
value: value,
8787
);
8888
}
8989
}
9090
if (value is List) {
91-
return EnumRuleConfig(
91+
return EnumRule(
9292
severity: severity,
9393
condition: condition,
9494
allowed: value.cast(),
9595
);
9696
}
97-
return ValueRuleConfig(
97+
return ValueRule(
9898
severity: severity,
9999
condition: condition,
100100
value: value,
101101
);
102102
}
103103

104-
RuleConfigSeverity _extractRuleConfigSeverity(int severity) {
105-
if (severity < 0 || severity > RuleConfigSeverity.values.length - 1) {
104+
RuleSeverity _extractRuleSeverity(int severity) {
105+
if (severity < 0 || severity > RuleSeverity.values.length - 1) {
106106
throw Exception(
107-
'rule severity can only be 0..${RuleConfigSeverity.values.length - 1}');
107+
'rule severity can only be 0..${RuleSeverity.values.length - 1}');
108108
}
109-
return RuleConfigSeverity.values[severity];
109+
return RuleSeverity.values[severity];
110110
}
111111

112-
RuleConfigCondition _extractRuleConfigCondition(String condition) {
113-
var allowed = RuleConfigCondition.values.map((e) => e.name).toList();
112+
RuleCondition _extractRuleCondition(String condition) {
113+
var allowed = RuleCondition.values.map((e) => e.name).toList();
114114
final index = allowed.indexOf(condition);
115115
if (index == -1) {
116116
throw Exception('rule condition can only one of $allowed');
117117
}
118-
return RuleConfigCondition.values[index];
118+
return RuleCondition.values[index];
119119
}
120120

121121
Case _extractCase(String name) {

lib/src/rules.dart

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'types/case.dart';
33
import 'types/commit.dart';
44
import 'types/rule.dart';
55

6-
Map<String, Rule> get supportedRules => {
6+
Map<String, RuleFunction> get supportedRules => {
77
'type-case': caseRule(CommitComponent.type),
88
'type-empty': emptyRule(CommitComponent.type),
99
'type-enum': enumRule(CommitComponent.type),
@@ -39,14 +39,14 @@ Map<String, Rule> get supportedRules => {
3939
};
4040

4141
/// Build full stop rule for commit component.
42-
Rule fullStopRule(CommitComponent component) {
43-
return (Commit commit, RuleConfig config) {
44-
if (config is! ValueRuleConfig) {
42+
RuleFunction fullStopRule(CommitComponent component) {
43+
return (Commit commit, Rule config) {
44+
if (config is! ValueRule) {
4545
throw Exception('$config is not ValueRuleConfig<String>');
4646
}
4747
final raw = commit.componentRaw(component);
4848
final result = raw != null && ensureFullStop(raw, config.value);
49-
final negated = config.condition == RuleConfigCondition.never;
49+
final negated = config.condition == RuleCondition.never;
5050
return RuleOutcome(
5151
valid: negated ? !result : result,
5252
message: [
@@ -59,11 +59,11 @@ Rule fullStopRule(CommitComponent component) {
5959
}
6060

6161
/// Build leanding blank rule for commit component.
62-
Rule leadingBlankRule(CommitComponent component) {
63-
return (Commit commit, RuleConfig config) {
62+
RuleFunction leadingBlankRule(CommitComponent component) {
63+
return (Commit commit, Rule config) {
6464
final raw = commit.componentRaw(component);
6565
final result = raw != null && ensureLeadingBlank(raw);
66-
final negated = config.condition == RuleConfigCondition.never;
66+
final negated = config.condition == RuleCondition.never;
6767
return RuleOutcome(
6868
valid: negated ? !result : result,
6969
message: [
@@ -76,11 +76,11 @@ Rule leadingBlankRule(CommitComponent component) {
7676
}
7777

7878
/// Build leanding blank rule for commit component.
79-
Rule emptyRule(CommitComponent component) {
80-
return (Commit commit, RuleConfig config) {
79+
RuleFunction emptyRule(CommitComponent component) {
80+
return (Commit commit, Rule config) {
8181
final raw = commit.componentRaw(component);
8282
final result = ensureEmpty(raw);
83-
final negated = config.condition == RuleConfigCondition.never;
83+
final negated = config.condition == RuleCondition.never;
8484
return RuleOutcome(
8585
valid: negated ? !result : result,
8686
message:
@@ -90,14 +90,14 @@ Rule emptyRule(CommitComponent component) {
9090
}
9191

9292
/// Build case rule for commit component.
93-
Rule caseRule(CommitComponent component) {
94-
return (Commit commit, RuleConfig config) {
95-
if (config is! CaseRuleConfig) {
93+
RuleFunction caseRule(CommitComponent component) {
94+
return (Commit commit, Rule config) {
95+
if (config is! CaseRule) {
9696
throw Exception('$config is not CaseRuleConfig');
9797
}
9898
final raw = commit.componentRaw(component);
9999
final result = raw != null && ensureCase(raw, config.type);
100-
final negated = config.condition == RuleConfigCondition.never;
100+
final negated = config.condition == RuleCondition.never;
101101
return RuleOutcome(
102102
valid: negated ? !result : result,
103103
message: [
@@ -110,14 +110,14 @@ Rule caseRule(CommitComponent component) {
110110
}
111111

112112
/// Build max length rule for commit component.
113-
Rule maxLengthRule(CommitComponent component) {
114-
return (Commit commit, RuleConfig config) {
115-
if (config is! LengthRuleConfig) {
113+
RuleFunction maxLengthRule(CommitComponent component) {
114+
return (Commit commit, Rule config) {
115+
if (config is! LengthRule) {
116116
throw Exception('$config is not LengthRuleConfig');
117117
}
118118
final raw = commit.componentRaw(component);
119119
final result = raw != null && ensureMaxLength(raw, config.length);
120-
final negated = config.condition == RuleConfigCondition.never;
120+
final negated = config.condition == RuleCondition.never;
121121
return RuleOutcome(
122122
valid: negated ? !result : result,
123123
message: [
@@ -130,14 +130,14 @@ Rule maxLengthRule(CommitComponent component) {
130130
}
131131

132132
/// Build max line length rule for commit component.
133-
Rule maxLineLengthRule(CommitComponent component) {
134-
return (Commit commit, RuleConfig config) {
135-
if (config is! LengthRuleConfig) {
133+
RuleFunction maxLineLengthRule(CommitComponent component) {
134+
return (Commit commit, Rule config) {
135+
if (config is! LengthRule) {
136136
throw Exception('$config is not LengthRuleConfig');
137137
}
138138
final raw = commit.componentRaw(component);
139139
final result = raw != null && ensureMaxLineLength(raw, config.length);
140-
final negated = config.condition == RuleConfigCondition.never;
140+
final negated = config.condition == RuleCondition.never;
141141
return RuleOutcome(
142142
valid: negated ? !result : result,
143143
message: [
@@ -150,14 +150,14 @@ Rule maxLineLengthRule(CommitComponent component) {
150150
}
151151

152152
/// Build min length rule for commit component.
153-
Rule minLengthRule(CommitComponent component) {
154-
return (Commit commit, RuleConfig config) {
155-
if (config is! LengthRuleConfig) {
153+
RuleFunction minLengthRule(CommitComponent component) {
154+
return (Commit commit, Rule config) {
155+
if (config is! LengthRule) {
156156
throw Exception('$config is not LengthRuleConfig');
157157
}
158158
final raw = commit.componentRaw(component);
159159
final result = raw != null && ensureMinLength(raw, config.length);
160-
final negated = config.condition == RuleConfigCondition.never;
160+
final negated = config.condition == RuleCondition.never;
161161
return RuleOutcome(
162162
valid: negated ? !result : result,
163163
message: [
@@ -169,14 +169,14 @@ Rule minLengthRule(CommitComponent component) {
169169
};
170170
}
171171

172-
Rule enumRule(CommitComponent component) {
173-
return (Commit commit, RuleConfig config) {
174-
if (config is! EnumRuleConfig) {
172+
RuleFunction enumRule(CommitComponent component) {
173+
return (Commit commit, Rule config) {
174+
if (config is! EnumRule) {
175175
throw Exception('$config is not EnumRuleConfig');
176176
}
177177
final raw = commit.componentRaw(component);
178178
final result = ensureEnum(raw, config.allowed);
179-
final negated = config.condition == RuleConfigCondition.never;
179+
final negated = config.condition == RuleCondition.never;
180180
return RuleOutcome(
181181
valid: negated ? !result : result,
182182
message: [

0 commit comments

Comments
 (0)