Skip to content

Commit 5d30604

Browse files
authored
Fix last_files_match_only to evaluate per file instead of globally (#135)
Previously, when last_files_match_only was enabled, every time a pattern matched any changed file, all previously accumulated reviewers were cleared. This meant the last pattern that matched ANY file in the PR determined all reviewers, which was wrong when different files had different last-matching patterns. Now, for each changed file independently, we find its last matching pattern's reviewers, then union the results across all files. This correctly handles the case where e.g. a generated file overrides reviewers to [] while a non-generated file in the same directory still matches the wildcard pattern's reviewers.
1 parent 9aaa2de commit 5d30604

File tree

3 files changed

+78
-17
lines changed

3 files changed

+78
-17
lines changed

dist/index.js

Lines changed: 20 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/reviewer.js

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,28 @@ function identify_reviewers_by_changed_files({ config, changed_files, excludes =
4848
return [];
4949
}
5050

51-
const matching_reviewers = [];
52-
53-
Object.entries(config.files).forEach(([ glob_pattern, reviewers ]) => {
54-
if (changed_files.some((changed_file) => minimatch(changed_file, glob_pattern))) {
55-
if (last_files_match_only) {
56-
matching_reviewers.length = 0; // clear previous matches
51+
let matching_reviewers;
52+
53+
if (last_files_match_only) {
54+
const file_patterns = Object.entries(config.files);
55+
const per_file_reviewers = changed_files.flatMap((changed_file) => {
56+
let last_matching_reviewers = null;
57+
file_patterns.forEach(([ glob_pattern, reviewers ]) => {
58+
if (minimatch(changed_file, glob_pattern)) {
59+
last_matching_reviewers = reviewers;
60+
}
61+
});
62+
return last_matching_reviewers || [];
63+
});
64+
matching_reviewers = per_file_reviewers;
65+
} else {
66+
matching_reviewers = [];
67+
Object.entries(config.files).forEach(([ glob_pattern, reviewers ]) => {
68+
if (changed_files.some((changed_file) => minimatch(changed_file, glob_pattern))) {
69+
matching_reviewers.push(...reviewers);
5770
}
58-
matching_reviewers.push(...reviewers);
59-
}
60-
});
71+
});
72+
}
6173

6274
const individuals = replace_groups_with_individuals({ reviewers: matching_reviewers, config });
6375

test/reviewer.test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,43 @@ describe('reviewer', function() {
126126
};
127127
expect(identify_reviewers_by_changed_files({ config: config_with_last_files_match_only, changed_files })).to.have.members([ 'mario', 'someone-specific' ]);
128128
});
129+
130+
context('with `last_files_match_only` and a specific file override with empty reviewers', function() {
131+
const override_config = {
132+
reviewers: {
133+
groups: {
134+
'team-a': [ 'alice', 'bob' ],
135+
},
136+
},
137+
files: {
138+
'src/**/*': [ 'team-a' ],
139+
'src/generated-file.dat': [],
140+
},
141+
options: {
142+
last_files_match_only: true,
143+
},
144+
};
145+
146+
it('returns reviewers when a file only matches the wildcard pattern', function() {
147+
const changed_files = [ 'src/main-code.js' ];
148+
expect(identify_reviewers_by_changed_files({ config: override_config, changed_files })).to.have.members([ 'alice', 'bob' ]);
149+
});
150+
151+
it('returns no reviewers when a file only matches the overriding empty pattern', function() {
152+
const changed_files = [ 'src/generated-file.dat' ];
153+
expect(identify_reviewers_by_changed_files({ config: override_config, changed_files })).to.deep.equal([]);
154+
});
155+
156+
it('returns no reviewers when all files are unmatched or overridden with empty reviewers', function() {
157+
const changed_files = [ 'unrelated-file.txt', 'src/generated-file.dat' ];
158+
expect(identify_reviewers_by_changed_files({ config: override_config, changed_files })).to.deep.equal([]);
159+
});
160+
161+
it('returns reviewers when one file matches the wildcard and another is overridden with empty reviewers', function() {
162+
const changed_files = [ 'src/main-code.js', 'src/generated-file.dat' ];
163+
expect(identify_reviewers_by_changed_files({ config: override_config, changed_files })).to.have.members([ 'alice', 'bob' ]);
164+
});
165+
});
129166
});
130167

131168
describe('identify_reviewers_by_author()', function() {

0 commit comments

Comments
 (0)