Skip to content

Commit ed19a16

Browse files
committed
Delete old /test help messages after a successful invocation
1 parent 99d79e9 commit ed19a16

File tree

5 files changed

+171
-6
lines changed

5 files changed

+171
-6
lines changed

pkg/pjutil/help.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ var (
3434
RetestWithTargetNote = "The `/retest` command does not accept any targets.\n"
3535
TargetNotFoundNote = "The specified target(s) for `/test` were not found.\n"
3636
ThereAreNoTestAllJobsNote = "No jobs can be run with `/test all`.\n"
37+
38+
availableRequiredTestsNote = "The following commands are available to trigger required jobs:"
39+
availableOptionalTestsNote = "The following commands are available to trigger optional jobs:"
3740
)
3841

3942
func MayNeedHelpComment(body string) bool {
@@ -60,6 +63,16 @@ func ShouldRespondWithHelp(body string, toRunOrSkip int) (bool, string) {
6063
}
6164
}
6265

66+
func IsHelpComment(body string) bool {
67+
return strings.Contains(body, TestWithoutTargetNote) ||
68+
strings.Contains(body, RetestWithTargetNote) ||
69+
strings.Contains(body, TargetNotFoundNote) ||
70+
strings.Contains(body, ThereAreNoTestAllJobsNote) ||
71+
// The response to "/test ?" has no header, so we have to search for these as well.
72+
strings.Contains(body, availableRequiredTestsNote) ||
73+
strings.Contains(body, availableOptionalTestsNote)
74+
}
75+
6376
// HelpMessage returns a user friendly help message with the
6477
//
6578
// available /test commands that can be triggered
@@ -79,10 +92,10 @@ func HelpMessage(org, repo, branch, note string, testAllNames, optionalTestComma
7992

8093
resp = note
8194
if requiredTestCommands.Len() > 0 {
82-
resp += fmt.Sprintf("The following commands are available to trigger required jobs:%s\n\n", listBuilder(requiredTestCommands))
95+
resp += availableRequiredTestsNote + listBuilder(requiredTestCommands) + "\n\n"
8396
}
8497
if optionalTestCommands.Len() > 0 {
85-
resp += fmt.Sprintf("The following commands are available to trigger optional jobs:%s\n\n", listBuilder(optionalTestCommands))
98+
resp += availableOptionalTestsNote + listBuilder(optionalTestCommands) + "\n\n"
8699
}
87100

88101
var testAllNote string

pkg/pjutil/help_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
Copyright 2025 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package pjutil
18+
19+
import (
20+
"testing"
21+
22+
"k8s.io/apimachinery/pkg/util/sets"
23+
)
24+
25+
func TestHelp(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
29+
// If the user writes userComment (which does not match IsHelpComment())
30+
userComment string
31+
32+
// then MayNeedHelpComment will return mayNeedHelp...
33+
mayNeedHelp bool
34+
35+
// and if true, ShouldRespondWithHelp(body, 1) will return (shouldRespond, note)
36+
shouldRespond bool
37+
note string
38+
39+
// and if true, note+helpMessageText should match the output of
40+
// HelpMessage() and should match IsHelpComment()
41+
}{
42+
{
43+
name: "empty comment",
44+
userComment: "",
45+
mayNeedHelp: false,
46+
},
47+
{
48+
name: "random comment",
49+
userComment: "This is a comment about testing.",
50+
mayNeedHelp: false,
51+
},
52+
{
53+
name: "valid test request",
54+
userComment: "/test e2e",
55+
mayNeedHelp: true,
56+
shouldRespond: false,
57+
},
58+
{
59+
name: "valid test all",
60+
userComment: "/test all",
61+
mayNeedHelp: true,
62+
shouldRespond: false,
63+
},
64+
{
65+
name: "valid retest",
66+
userComment: "/retest",
67+
mayNeedHelp: false,
68+
},
69+
{
70+
name: "empty test",
71+
userComment: "/test",
72+
mayNeedHelp: true,
73+
shouldRespond: true,
74+
note: TestWithoutTargetNote,
75+
},
76+
{
77+
name: "retest with target",
78+
userComment: "/retest e2e",
79+
mayNeedHelp: true,
80+
shouldRespond: true,
81+
note: RetestWithTargetNote,
82+
},
83+
{
84+
name: "/test ?",
85+
userComment: "/test ?",
86+
mayNeedHelp: true,
87+
shouldRespond: true,
88+
note: "",
89+
},
90+
}
91+
92+
required := sets.New("/test e2e", "/test e2e-serial", "/test unit")
93+
optional := sets.New("/test lint", "/test e2e-conformance-commodore64")
94+
all := required.Union(optional)
95+
helpBody := HelpMessage("", "", "", "", all, optional, required)
96+
97+
for _, tc := range tests {
98+
t.Run(tc.name, func(t *testing.T) {
99+
if IsHelpComment(tc.userComment) {
100+
t.Errorf("Expected IsHelpComment(%q) to return false, got true", tc.userComment)
101+
}
102+
mayNeedHelp := MayNeedHelpComment(tc.userComment)
103+
if mayNeedHelp != tc.mayNeedHelp {
104+
t.Errorf("Expected MayNeedHelpComment(%q) to return %v, got %v", tc.userComment, tc.mayNeedHelp, mayNeedHelp)
105+
}
106+
if !tc.mayNeedHelp {
107+
return
108+
}
109+
shouldRespond, note := ShouldRespondWithHelp(tc.userComment, 1)
110+
if shouldRespond != tc.shouldRespond {
111+
t.Errorf("Expected ShouldRespondWithHelp(%q) to return %v, got %v", tc.userComment, tc.shouldRespond, shouldRespond)
112+
}
113+
if !tc.shouldRespond {
114+
return
115+
}
116+
if note != tc.note {
117+
t.Errorf("Expected ShouldRespondWithHelp(%q) to return note %q, got %q", tc.userComment, tc.note, note)
118+
}
119+
120+
expectHelpMessage := tc.note + helpBody
121+
helpMessage := HelpMessage("", "", "", note, all, optional, required)
122+
if helpMessage != expectHelpMessage {
123+
t.Errorf("Expected HelpMessage() to return %q, got %q", expectHelpMessage, helpMessage)
124+
}
125+
if !IsHelpComment(helpMessage) {
126+
t.Errorf("Expected IsHelpComment(%q) to return true, got false", helpMessage)
127+
}
128+
})
129+
}
130+
}
131+

pkg/plugins/trigger/generic-comment.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ import (
3131
"sigs.k8s.io/prow/pkg/plugins"
3232
)
3333

34-
func handleGenericComment(c Client, trigger plugins.Trigger, gc github.GenericCommentEvent) error {
34+
type commentPruner interface {
35+
PruneComments(shouldPrune func(github.IssueComment) bool)
36+
}
37+
38+
func handleGenericComment(c Client, cp commentPruner, trigger plugins.Trigger, gc github.GenericCommentEvent) error {
3539
org := gc.Repo.Owner.Login
3640
repo := gc.Repo.Name
3741
number := gc.Number
@@ -174,6 +178,13 @@ func handleGenericComment(c Client, trigger plugins.Trigger, gc github.GenericCo
174178
}
175179
}
176180
}
181+
182+
// The user has successfully requested a test run, so delete any previous /test
183+
// help message.
184+
cp.PruneComments(func(comment github.IssueComment) bool {
185+
return pjutil.IsHelpComment(comment.Body)
186+
})
187+
177188
return RunRequestedWithLabels(c, pr, baseSHA, toTest, gc.GUID, additionalLabels)
178189
}
179190

pkg/plugins/trigger/generic-comment_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ func issueLabels(labels ...string) []string {
4747

4848
const shouldNotAddComment = "<none>"
4949

50+
type fakeCommentPruner struct{}
51+
52+
func (f *fakeCommentPruner) PruneComments(shouldPrune func(github.IssueComment) bool) {}
53+
5054
type testcase struct {
5155
name string
5256

@@ -1485,15 +1489,17 @@ func TestHandleGenericComment(t *testing.T) {
14851489
}
14861490
trigger.SetDefaults()
14871491

1492+
cp := &fakeCommentPruner{}
1493+
14881494
log.Printf("running case %s", tc.name)
14891495
// In some cases handleGenericComment can be called twice for the same event.
14901496
// For instance on Issue/PR creation and modification.
14911497
// Let's call it twice to ensure idempotency.
1492-
if err := handleGenericComment(c, trigger, event); err != nil {
1498+
if err := handleGenericComment(c, cp, trigger, event); err != nil {
14931499
t.Fatalf("%s: didn't expect error: %s", tc.name, err)
14941500
}
14951501
validate(t, fakeProwJobClient.Fake.Actions(), g, tc)
1496-
if err := handleGenericComment(c, trigger, event); err != nil {
1502+
if err := handleGenericComment(c, cp, trigger, event); err != nil {
14971503
t.Fatalf("%s: didn't expect error: %s", tc.name, err)
14981504
}
14991505
validate(t, fakeProwJobClient.Fake.Actions(), g, tc)

pkg/plugins/trigger/trigger.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,11 @@ func handlePullRequest(pc plugins.Agent, pr github.PullRequestEvent) error {
210210
}
211211

212212
func handleGenericCommentEvent(pc plugins.Agent, gc github.GenericCommentEvent) error {
213-
return handleGenericComment(getClient(pc), pc.PluginConfig.TriggerFor(gc.Repo.Owner.Login, gc.Repo.Name), gc)
213+
cp, err := pc.CommentPruner()
214+
if err != nil {
215+
return err
216+
}
217+
return handleGenericComment(getClient(pc), cp, pc.PluginConfig.TriggerFor(gc.Repo.Owner.Login, gc.Repo.Name), gc)
214218
}
215219

216220
func handlePush(pc plugins.Agent, pe github.PushEvent) error {

0 commit comments

Comments
 (0)