Skip to content

Commit d9d3660

Browse files
committed
Add the ability to ignore push events from certain users
1 parent 32555bd commit d9d3660

File tree

6 files changed

+184
-7
lines changed

6 files changed

+184
-7
lines changed

src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.kohsuke.accmod.restrictions.NoExternalUse;
3636
import org.kohsuke.stapler.AncestorInPath;
3737
import org.kohsuke.stapler.DataBoundConstructor;
38+
import org.kohsuke.stapler.DataBoundSetter;
3839
import org.kohsuke.stapler.Stapler;
3940
import org.slf4j.Logger;
4041
import org.slf4j.LoggerFactory;
@@ -67,10 +68,56 @@
6768
*/
6869
public class GitHubPushTrigger extends Trigger<Job<?, ?>> implements GitHubTrigger {
6970

71+
private String ignoredUsers;
72+
7073
@DataBoundConstructor
7174
public GitHubPushTrigger() {
7275
}
7376

77+
/**
78+
* Gets the newline-separated list of usernames to ignore.
79+
*
80+
* @return the ignored users list, or null if not configured
81+
* @since FIXME
82+
*/
83+
public String getIgnoredUsers() {
84+
return ignoredUsers;
85+
}
86+
87+
/**
88+
* Sets the newline-separated list of usernames whose push events should be ignored.
89+
* The list will be trimmed of leading/trailing whitespace.
90+
*
91+
* @param ignoredUsers the newline-separated list of usernames to ignore
92+
* @since FIXME
93+
*/
94+
@DataBoundSetter
95+
public void setIgnoredUsers(String ignoredUsers) {
96+
this.ignoredUsers = Util.fixEmptyAndTrim(ignoredUsers);
97+
}
98+
99+
/**
100+
* Checks if the given username should be ignored.
101+
* Username comparison is case-insensitive.
102+
*
103+
* @param username the username to check
104+
* @return true if the user should be ignored, false otherwise
105+
* @since FIXME
106+
*/
107+
public boolean isUserIgnored(String username) {
108+
if (isEmpty(ignoredUsers) || isEmpty(username)) {
109+
return false;
110+
}
111+
String[] ignoredUserArray = ignoredUsers.split("[\\r\\n]+");
112+
for (String ignoredUser : ignoredUserArray) {
113+
String trimmedUser = ignoredUser.trim();
114+
if (!trimmedUser.isEmpty() && trimmedUser.equalsIgnoreCase(username)) {
115+
return true;
116+
}
117+
}
118+
return false;
119+
}
120+
74121
/**
75122
* Called when a POST is made.
76123
*/

src/main/java/org/jenkinsci/plugins/github/webhook/subscriber/DefaultPushGHEventSubscriber.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,18 @@ public void run() {
9292
LOGGER.debug("Considering to poke {}", fullDisplayName);
9393
if (GitHubRepositoryNameContributor.parseAssociatedNames(job)
9494
.contains(changedRepository)) {
95-
LOGGER.info("Poked {}", fullDisplayName);
96-
trigger.onPost(GitHubTriggerEvent.create()
97-
.withTimestamp(event.getTimestamp())
98-
.withOrigin(event.getOrigin())
99-
.withTriggeredByUser(pusherName)
100-
.build()
101-
);
95+
if (trigger.isUserIgnored(pusherName)) {
96+
LOGGER.debug("Skipped {} because pusher '{}' is in the ignored users list",
97+
fullDisplayName, pusherName);
98+
} else {
99+
LOGGER.info("Poked {}", fullDisplayName);
100+
trigger.onPost(GitHubTriggerEvent.create()
101+
.withTimestamp(event.getTimestamp())
102+
.withOrigin(event.getOrigin())
103+
.withTriggeredByUser(pusherName)
104+
.build()
105+
);
106+
}
102107
} else {
103108
LOGGER.debug("Skipped {} because it doesn't have a matching repository.",
104109
fullDisplayName);

src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@ package com.cloudbees.jenkins.GitHubPushTrigger
22

33
import com.cloudbees.jenkins.GitHubPushTrigger
44

5+
def f = namespace(lib.FormTagLib)
6+
7+
f.advanced() {
8+
f.entry(title: _('Ignored Users'), field: 'ignoredUsers') {
9+
f.textarea()
10+
}
11+
}
12+
513
tr {
614
td(colspan: 4) {
715
def url = descriptor.getCheckMethod('hookRegistered').toCheckUrl()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<div>
2+
A newline-separated list of (case-insensitive) GitHub usernames whose push events should be ignored.
3+
<br/>
4+
When a push event is received from a user in this list, the trigger will not poll for changes.
5+
<br/>
6+
<br/>
7+
Example:
8+
<pre>renovate-bot
9+
dependabot[bot]
10+
some-automation-user</pre>
11+
</div>

src/test/java/com/cloudbees/jenkins/GitHubPushTriggerTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import static org.hamcrest.MatcherAssert.assertThat;
2727
import static org.hamcrest.Matchers.is;
2828
import static org.jenkinsci.plugins.github.webhook.subscriber.DefaultPushGHEventListenerTest.TRIGGERED_BY_USER_FROM_RESOURCE;
29+
import static org.junit.Assert.assertFalse;
30+
import static org.junit.Assert.assertTrue;
2931

3032
/**
3133
* @author lanwen (Merkushev Kirill)
@@ -96,4 +98,39 @@ public void shouldReturnOkOnNoAnyProblem() throws Exception {
9698
FormValidation validation = descriptor.doCheckHookRegistered(job);
9799
assertThat("all ok", validation.kind, is(FormValidation.Kind.OK));
98100
}
101+
102+
@Test
103+
public void shouldIgnoreSingleUser() {
104+
GitHubPushTrigger trigger = new GitHubPushTrigger();
105+
trigger.setIgnoredUsers("ignored-user");
106+
107+
assertTrue("user should be ignored", trigger.isUserIgnored("ignored-user"));
108+
assertTrue("user should be ignored", trigger.isUserIgnored("IGNORED-user"));
109+
assertFalse("user should not be ignored", trigger.isUserIgnored("another-user"));
110+
assertFalse("user should not be ignored", trigger.isUserIgnored(""));
111+
assertFalse("user should not be ignored", trigger.isUserIgnored(null));
112+
}
113+
114+
@Test
115+
public void shouldIgnoreMultipleUsers() {
116+
GitHubPushTrigger trigger = new GitHubPushTrigger();
117+
trigger.setIgnoredUsers(" user1 \nUsEr2\nuser3");
118+
119+
assertTrue("user should be ignored", trigger.isUserIgnored("user1"));
120+
assertTrue("user should be ignored", trigger.isUserIgnored("user2"));
121+
assertTrue("user should be ignored", trigger.isUserIgnored("USER3"));
122+
assertFalse("user should not be ignored", trigger.isUserIgnored("user4"));
123+
assertFalse("user should not be ignored", trigger.isUserIgnored(""));
124+
assertFalse("user should not be ignored", trigger.isUserIgnored(null));
125+
}
126+
127+
@Test
128+
public void shouldHandleEmptyIgnoredUsers() {
129+
GitHubPushTrigger trigger = new GitHubPushTrigger();
130+
trigger.setIgnoredUsers("");
131+
132+
assertFalse("user should not be ignored", trigger.isUserIgnored("user4"));
133+
assertFalse("user should not be ignored", trigger.isUserIgnored(""));
134+
assertFalse("user should not be ignored", trigger.isUserIgnored(null));
135+
}
99136
}

src/test/java/org/jenkinsci/plugins/github/webhook/subscriber/DefaultPushGHEventListenerTest.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,73 @@ public void shouldNotReceivePushHookOnWorkflowWithNoBuilds() throws Exception {
133133

134134
verify(trigger, never()).onPost(Mockito.any(GitHubTriggerEvent.class));
135135
}
136+
137+
@Test
138+
@WithoutJenkins
139+
public void shouldNotTriggerWhenUserIsIgnored() {
140+
GitHubPushTrigger trigger = mock(GitHubPushTrigger.class);
141+
when(trigger.isUserIgnored(eq(TRIGGERED_BY_USER_FROM_RESOURCE))).thenReturn(true);
142+
143+
FreeStyleProject prj = mock(FreeStyleProject.class);
144+
when(prj.getTriggers()).thenReturn(
145+
Collections.singletonMap(new GitHubPushTrigger.DescriptorImpl(), trigger));
146+
when(prj.getSCMs()).thenAnswer(unused -> Collections.singletonList(GIT_SCM_FROM_RESOURCE));
147+
when(prj.getFullDisplayName()).thenReturn("test-job");
148+
149+
GHSubscriberEvent subscriberEvent =
150+
new GHSubscriberEvent("shouldNotTriggerWhenUserIsIgnored", GHEvent.PUSH, classpath("payloads/push.json"));
151+
152+
Jenkins jenkins = mock(Jenkins.class);
153+
when(jenkins.getAllItems(Item.class)).thenReturn(Collections.singletonList(prj));
154+
155+
ExtensionList<GitHubRepositoryNameContributor> extensionList = mock(ExtensionList.class);
156+
List<GitHubRepositoryNameContributor> gitHubRepositoryNameContributorList =
157+
Collections.singletonList(new GitHubRepositoryNameContributor.FromSCM());
158+
when(extensionList.iterator()).thenReturn(gitHubRepositoryNameContributorList.iterator());
159+
when(jenkins.getExtensionList(GitHubRepositoryNameContributor.class)).thenReturn(extensionList);
160+
161+
try (MockedStatic<Jenkins> mockedJenkins = mockStatic(Jenkins.class)) {
162+
mockedJenkins.when(Jenkins::getInstance).thenReturn(jenkins);
163+
new DefaultPushGHEventSubscriber().onEvent(subscriberEvent);
164+
}
165+
166+
verify(trigger, never()).onPost(Mockito.any(GitHubTriggerEvent.class));
167+
}
168+
169+
@Test
170+
@WithoutJenkins
171+
public void shouldTriggerWhenUserIsNotIgnored() {
172+
GitHubPushTrigger trigger = mock(GitHubPushTrigger.class);
173+
when(trigger.isUserIgnored(eq(TRIGGERED_BY_USER_FROM_RESOURCE))).thenReturn(false);
174+
175+
FreeStyleProject prj = mock(FreeStyleProject.class);
176+
when(prj.getTriggers()).thenReturn(
177+
Collections.singletonMap(new GitHubPushTrigger.DescriptorImpl(), trigger));
178+
when(prj.getSCMs()).thenAnswer(unused -> Collections.singletonList(GIT_SCM_FROM_RESOURCE));
179+
when(prj.getFullDisplayName()).thenReturn("test-job");
180+
181+
GHSubscriberEvent subscriberEvent =
182+
new GHSubscriberEvent("shouldTriggerWhenUserIsNotIgnored", GHEvent.PUSH, classpath("payloads/push.json"));
183+
184+
Jenkins jenkins = mock(Jenkins.class);
185+
when(jenkins.getAllItems(Item.class)).thenReturn(Collections.singletonList(prj));
186+
187+
ExtensionList<GitHubRepositoryNameContributor> extensionList = mock(ExtensionList.class);
188+
List<GitHubRepositoryNameContributor> gitHubRepositoryNameContributorList =
189+
Collections.singletonList(new GitHubRepositoryNameContributor.FromSCM());
190+
when(extensionList.iterator()).thenReturn(gitHubRepositoryNameContributorList.iterator());
191+
when(jenkins.getExtensionList(GitHubRepositoryNameContributor.class)).thenReturn(extensionList);
192+
193+
try (MockedStatic<Jenkins> mockedJenkins = mockStatic(Jenkins.class)) {
194+
mockedJenkins.when(Jenkins::getInstance).thenReturn(jenkins);
195+
new DefaultPushGHEventSubscriber().onEvent(subscriberEvent);
196+
}
197+
198+
verify(trigger).onPost(eq(GitHubTriggerEvent.create()
199+
.withTimestamp(subscriberEvent.getTimestamp())
200+
.withOrigin("shouldTriggerWhenUserIsNotIgnored")
201+
.withTriggeredByUser(TRIGGERED_BY_USER_FROM_RESOURCE)
202+
.build()
203+
));
204+
}
136205
}

0 commit comments

Comments
 (0)