Skip to content

Commit ff8eb45

Browse files
committed
Add the ability to ignore push events from certain users
1 parent 4127935 commit ff8eb45

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)
@@ -97,4 +99,39 @@ void shouldReturnOkOnNoAnyProblem() throws Exception {
9799
FormValidation validation = descriptor.doCheckHookRegistered(job);
98100
assertThat("all ok", validation.kind, is(FormValidation.Kind.OK));
99101
}
102+
103+
@Test
104+
public void shouldIgnoreSingleUser() {
105+
GitHubPushTrigger trigger = new GitHubPushTrigger();
106+
trigger.setIgnoredUsers("ignored-user");
107+
108+
assertTrue("user should be ignored", trigger.isUserIgnored("ignored-user"));
109+
assertTrue("user should be ignored", trigger.isUserIgnored("IGNORED-user"));
110+
assertFalse("user should not be ignored", trigger.isUserIgnored("another-user"));
111+
assertFalse("user should not be ignored", trigger.isUserIgnored(""));
112+
assertFalse("user should not be ignored", trigger.isUserIgnored(null));
113+
}
114+
115+
@Test
116+
public void shouldIgnoreMultipleUsers() {
117+
GitHubPushTrigger trigger = new GitHubPushTrigger();
118+
trigger.setIgnoredUsers(" user1 \nUsEr2\nuser3");
119+
120+
assertTrue("user should be ignored", trigger.isUserIgnored("user1"));
121+
assertTrue("user should be ignored", trigger.isUserIgnored("user2"));
122+
assertTrue("user should be ignored", trigger.isUserIgnored("USER3"));
123+
assertFalse("user should not be ignored", trigger.isUserIgnored("user4"));
124+
assertFalse("user should not be ignored", trigger.isUserIgnored(""));
125+
assertFalse("user should not be ignored", trigger.isUserIgnored(null));
126+
}
127+
128+
@Test
129+
public void shouldHandleEmptyIgnoredUsers() {
130+
GitHubPushTrigger trigger = new GitHubPushTrigger();
131+
trigger.setIgnoredUsers("");
132+
133+
assertFalse("user should not be ignored", trigger.isUserIgnored("user4"));
134+
assertFalse("user should not be ignored", trigger.isUserIgnored(""));
135+
assertFalse("user should not be ignored", trigger.isUserIgnored(null));
136+
}
100137
}

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
@@ -140,4 +140,73 @@ void shouldNotReceivePushHookOnWorkflowWithNoBuilds() throws Exception {
140140

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

0 commit comments

Comments
 (0)