Skip to content

Commit fd299d7

Browse files
authored
Evaluate PR Body independently from comments (#69)
* Evaluate PR Body independently from comments Right now we compare comment substrings on both comments and the PR body. In some cases, it should be possible to lock down evaluation to only the PR body, instead of comments _and_ the pr body. Users who submit PRs do not have write access to the repo, so they cannot add a label. For comments, anyone with read access to the repo can add a comment on any PR. However, only someone with write access (or the Author) can edit the PR description. * Revert breaking changes to body matching add further test cases * Update wording in the README The existing phrasing doesn't accurately describe current operation
1 parent 3b28cc6 commit fd299d7

4 files changed

Lines changed: 127 additions & 1 deletion

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ merge:
3333
# "labels" is a list of labels that must be matched to whitelist a PR for merging (case-insensitive)
3434
labels: ["merge when ready"]
3535

36-
# "comment_substrings" matches on substrings in comments
36+
# "comment_substrings" matches on substrings in comments or the pull request body
3737
comment_substrings: ["==MERGE_WHEN_READY=="]
3838

39+
# "pr_body_substrings" matches on substrings in the PR body
40+
pr_body_substrings: ["==MERGE_WHEN_READY=="]
41+
3942
# "blacklist" defines how to exclude PRs from evaluation and merging
4043
blacklist:
4144

bulldozer/config_v1.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Signals struct {
3131
Labels []string `yaml:"labels"`
3232
CommentSubstrings []string `yaml:"comment_substrings"`
3333
Comments []string `yaml:"comments"`
34+
PRBodySubstrings []string `yaml:"pr_body_substrings"`
3435
}
3536

3637
func (s *Signals) Enabled() bool {

bulldozer/evaluate.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ func IsPRBlacklisted(ctx context.Context, pullCtx pull.Context, config Signals)
6969
}
7070
}
7171

72+
for _, blacklistedSubstring := range config.PRBodySubstrings {
73+
if strings.Contains(body, blacklistedSubstring) {
74+
return true, fmt.Sprintf("PR body matches one of specified blacklist substrings: %q", blacklistedSubstring), nil
75+
}
76+
}
77+
7278
return false, "no matching blacklist found", nil
7379
}
7480

@@ -116,6 +122,12 @@ func IsPRWhitelisted(ctx context.Context, pullCtx pull.Context, config Signals)
116122
}
117123
}
118124

125+
for _, whitelistedSubstring := range config.PRBodySubstrings {
126+
if strings.Contains(body, whitelistedSubstring) {
127+
return true, fmt.Sprintf("PR body matches one of specified whitelist substrings: %q", whitelistedSubstring), nil
128+
}
129+
}
130+
119131
return false, "no matching whitelist found", nil
120132
}
121133

bulldozer/evaluate_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ func TestSimpleXListed(t *testing.T) {
3131
Labels: []string{"LABEL_MERGE"},
3232
Comments: []string{"FULL_COMMENT_PLZ_MERGE"},
3333
CommentSubstrings: []string{":+1:"},
34+
PRBodySubstrings: []string{"BODY_MERGE_PLZ"},
3435
},
3536
Blacklist: Signals{
3637
Labels: []string{"LABEL_NOMERGE"},
3738
Comments: []string{"NO_WAY"},
3839
CommentSubstrings: []string{":-1:"},
40+
PRBodySubstrings: []string{"BODY_NOMERGE"},
3941
},
4042
}
4143

@@ -167,6 +169,114 @@ func TestSimpleXListed(t *testing.T) {
167169
assert.True(t, actualWhitelist)
168170
assert.Equal(t, "PR label matches one of specified whitelist labels: \"LABEL_MERGE\"", actualWhitelistReason)
169171
})
172+
173+
t.Run("bodyCausesWhitelist", func(t *testing.T) {
174+
pc := &pulltest.MockPullContext{
175+
BodyValue: "My PR Body\n\n\n BODY_MERGE_PLZ",
176+
}
177+
178+
actualWhitelist, actualWhitelistReason, err := IsPRWhitelisted(ctx, pc, mergeConfig.Whitelist)
179+
require.Nil(t, err)
180+
assert.True(t, actualWhitelist)
181+
assert.Equal(t, "PR body matches one of specified whitelist substrings: \"BODY_MERGE_PLZ\"", actualWhitelistReason)
182+
})
183+
184+
t.Run("bodyCausesCommentSubstringWhitelist", func(t *testing.T) {
185+
pc := &pulltest.MockPullContext{
186+
BodyValue: "My PR Body\n\n\n :+1:",
187+
}
188+
189+
actualWhitelist, actualWhitelistReason, err := IsPRWhitelisted(ctx, pc, mergeConfig.Whitelist)
190+
require.Nil(t, err)
191+
assert.True(t, actualWhitelist)
192+
assert.Equal(t, "PR body matches one of specified whitelist comment substrings: \":+1:\"", actualWhitelistReason)
193+
})
194+
195+
t.Run("bodyCausesCommentWhitelist", func(t *testing.T) {
196+
pc := &pulltest.MockPullContext{
197+
BodyValue: "FULL_COMMENT_PLZ_MERGE",
198+
}
199+
200+
actualWhitelist, actualWhitelistReason, err := IsPRWhitelisted(ctx, pc, mergeConfig.Whitelist)
201+
require.Nil(t, err)
202+
assert.True(t, actualWhitelist)
203+
assert.Equal(t, "PR body matches one of specified whitelist comments: \"FULL_COMMENT_PLZ_MERGE\"", actualWhitelistReason)
204+
})
205+
206+
t.Run("bodyCausesBlacklist", func(t *testing.T) {
207+
pc := &pulltest.MockPullContext{
208+
BodyValue: "My PR Body\n\n\n BODY_NOMERGE",
209+
}
210+
211+
actualBlacklist, actualBlacklistReason, err := IsPRBlacklisted(ctx, pc, mergeConfig.Blacklist)
212+
require.Nil(t, err)
213+
assert.True(t, actualBlacklist)
214+
assert.Equal(t, "PR body matches one of specified blacklist substrings: \"BODY_NOMERGE\"", actualBlacklistReason)
215+
})
216+
217+
t.Run("bodyCausesCommentSubstringBlacklist", func(t *testing.T) {
218+
pc := &pulltest.MockPullContext{
219+
BodyValue: "My PR Body\n\n\n :-1:",
220+
}
221+
222+
actualBlacklist, actualBlacklistReason, err := IsPRBlacklisted(ctx, pc, mergeConfig.Blacklist)
223+
require.Nil(t, err)
224+
assert.True(t, actualBlacklist)
225+
assert.Equal(t, "PR body matches one of specified blacklist comment substrings: \":-1:\"", actualBlacklistReason)
226+
})
227+
228+
t.Run("bodyCausesCommentBlacklist", func(t *testing.T) {
229+
pc := &pulltest.MockPullContext{
230+
BodyValue: "NO_WAY",
231+
}
232+
233+
actualBlacklist, actualBlacklistReason, err := IsPRBlacklisted(ctx, pc, mergeConfig.Blacklist)
234+
require.Nil(t, err)
235+
assert.True(t, actualBlacklist)
236+
assert.Equal(t, "PR body matches one of specified blacklist comments: \"NO_WAY\"", actualBlacklistReason)
237+
})
238+
239+
t.Run("errBodyFailsClosedBlacklist", func(t *testing.T) {
240+
pc := &pulltest.MockPullContext{
241+
BodyValue: "My PR Body",
242+
BodyErrValue: errors.New("failure"),
243+
}
244+
245+
actualBlacklist, _, err := IsPRBlacklisted(ctx, pc, mergeConfig.Blacklist)
246+
require.NotNil(t, err)
247+
assert.True(t, actualBlacklist)
248+
})
249+
250+
t.Run("errBodyFailsClosedWhitelist", func(t *testing.T) {
251+
pc := &pulltest.MockPullContext{
252+
BodyValue: "My PR Body",
253+
BodyErrValue: errors.New("failure"),
254+
}
255+
256+
actualWhitelist, _, err := IsPRWhitelisted(ctx, pc, mergeConfig.Whitelist)
257+
require.NotNil(t, err)
258+
assert.False(t, actualWhitelist)
259+
})
260+
261+
t.Run("errLabelFailsClosedWhitelist", func(t *testing.T) {
262+
pc := &pulltest.MockPullContext{
263+
LabelErrValue: errors.New("failure"),
264+
}
265+
266+
actualWhitelist, _, err := IsPRWhitelisted(ctx, pc, mergeConfig.Whitelist)
267+
require.NotNil(t, err)
268+
assert.False(t, actualWhitelist)
269+
})
270+
271+
t.Run("errCommentsFailsClosedWhitelist", func(t *testing.T) {
272+
pc := &pulltest.MockPullContext{
273+
CommentErrValue: errors.New("failure"),
274+
}
275+
276+
actualWhitelist, _, err := IsPRWhitelisted(ctx, pc, mergeConfig.Whitelist)
277+
require.NotNil(t, err)
278+
assert.False(t, actualWhitelist)
279+
})
170280
}
171281

172282
func TestShouldMerge(t *testing.T) {

0 commit comments

Comments
 (0)