diff --git a/src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java b/src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java index c386afe0f..2c41178d8 100644 --- a/src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java +++ b/src/main/java/com/cloudbees/jenkins/GitHubPushTrigger.java @@ -21,6 +21,8 @@ import jenkins.model.Jenkins; import jenkins.model.ParameterizedJobMixIn; import jenkins.triggers.SCMTriggerItem.SCMTriggerItems; +import net.sf.json.JSONObject; + import org.apache.commons.jelly.XMLOutput; import org.jenkinsci.plugins.github.GitHubPlugin; import org.jenkinsci.plugins.github.admin.GitHubHookRegisterProblemMonitor; @@ -56,9 +58,51 @@ * @author Kohsuke Kawaguchi */ public class GitHubPushTrigger extends Trigger> implements GitHubTrigger { + private String pushBy; + private final String ignorablePusher; @DataBoundConstructor + public GitHubPushTrigger(final String ignorablePusher) { + this.ignorablePusher = ignorablePusher; + } + public GitHubPushTrigger() { + this(null); + } + + String pushBy() { + return pushBy; + } + + /** + * Gets the regular expression to which the name or email of an ignorable pusher is to be matched. + * + * @return the regular expression to which the name or email of an ignorable pusher is to be matched. + */ + public String getIgnorablePusher() { + return ignorablePusher; + } + + /** + * @param payload payload of gh-event. Never blank + * @return true if the regular expression to which the pusher name + * or email of a payload is to be matched, false otherwise + */ + public boolean ignores(final JSONObject payload) { + if (ignorablePusher != null && !ignorablePusher.isEmpty()) { + final JSONObject pusher = payload.getJSONObject("pusher"); + + for (final String key : new String[] {"name", "email"}) { + final String value = pusher.getString(key); + + if (value != null && value.matches(ignorablePusher)) { + LOGGER.info("Ignoring pusher [{}] ...", pusher); + return true; + } + } + } + + return false; } /** @@ -73,7 +117,7 @@ public void onPost() { * Called when a POST is made. */ public void onPost(String triggeredByUser) { - final String pushBy = triggeredByUser; + pushBy = triggeredByUser; DescriptorImpl d = getDescriptor(); d.checkThreadPoolSizeAndUpdateIfNecessary(); d.queue.execute(new Runnable() { diff --git a/src/main/java/org/jenkinsci/plugins/github/webhook/subscriber/DefaultPushGHEventSubscriber.java b/src/main/java/org/jenkinsci/plugins/github/webhook/subscriber/DefaultPushGHEventSubscriber.java index bee94ab34..db467e72b 100644 --- a/src/main/java/org/jenkinsci/plugins/github/webhook/subscriber/DefaultPushGHEventSubscriber.java +++ b/src/main/java/org/jenkinsci/plugins/github/webhook/subscriber/DefaultPushGHEventSubscriber.java @@ -3,7 +3,6 @@ import com.cloudbees.jenkins.GitHubPushTrigger; import com.cloudbees.jenkins.GitHubRepositoryName; import com.cloudbees.jenkins.GitHubRepositoryNameContributor; -import com.cloudbees.jenkins.GitHubTrigger; import com.cloudbees.jenkins.GitHubWebHook; import hudson.Extension; import hudson.model.Job; @@ -61,7 +60,7 @@ protected Set events() { */ @Override protected void onEvent(GHEvent event, String payload) { - JSONObject json = JSONObject.fromObject(payload); + final JSONObject json = JSONObject.fromObject(payload); String repoUrl = json.getJSONObject("repository").getString("url"); final String pusherName = json.getJSONObject("pusher").getString("name"); @@ -76,8 +75,8 @@ protected void onEvent(GHEvent event, String payload) { @Override public void run() { for (Job job : Jenkins.getInstance().getAllItems(Job.class)) { - GitHubTrigger trigger = triggerFrom(job, GitHubPushTrigger.class); - if (trigger != null) { + final GitHubPushTrigger trigger = triggerFrom(job, GitHubPushTrigger.class); + if (trigger != null && !trigger.ignores(json)) { LOGGER.debug("Considering to poke {}", job.getFullDisplayName()); if (GitHubRepositoryNameContributor.parseAssociatedNames(job).contains(changedRepository)) { LOGGER.info("Poked {}", job.getFullDisplayName()); diff --git a/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.groovy b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.groovy index c9a140f5c..4530a6434 100644 --- a/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.groovy +++ b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.groovy @@ -2,6 +2,16 @@ package com.cloudbees.jenkins.GitHubPushTrigger import com.cloudbees.jenkins.GitHubPushTrigger +def f = namespace(lib.FormTagLib) + +tr { + td(colspan: 4) { + f.entry(title: _("Ignorable Pusher"), field: "ignorablePusher") { + f.textbox() + } + } +} + tr { td(colspan: 4) { div(id: 'gh-hooks-warn') diff --git a/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.properties b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.properties new file mode 100644 index 000000000..de7edbd49 --- /dev/null +++ b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config.properties @@ -0,0 +1 @@ +github.ignorablePusher=Ignorable pusher diff --git a/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config_de.properties b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config_de.properties new file mode 100644 index 000000000..fc398c9dc --- /dev/null +++ b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/config_de.properties @@ -0,0 +1 @@ +github.ignorablePusher=Ignorierbarer Pusher diff --git a/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/help-ignorablePusher.html b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/help-ignorablePusher.html new file mode 100644 index 000000000..f7320f708 --- /dev/null +++ b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/help-ignorablePusher.html @@ -0,0 +1,3 @@ +
+ This defines a regular expression to which the name or email of an ignorable pusher is to be matched. Such a push will be ignored. Usually a preceding push of Jenkins him self could be prevented that way. +
diff --git a/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/help-ignorablePusher_de.html b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/help-ignorablePusher_de.html new file mode 100644 index 000000000..a63a4af8d --- /dev/null +++ b/src/main/resources/com/cloudbees/jenkins/GitHubPushTrigger/help-ignorablePusher_de.html @@ -0,0 +1,3 @@ +
+ Hier wird ein regulärer Ausdruck definiert, der zu dem Namen oder der E-Mail eines ignorierbaren Pushers paßt. Solch ein Push wird ignoriert. Üblicherweise kann auf diese Weise damit ein vorhergehender Push von Jenkins selber verhindert werden. +
diff --git a/src/test/java/com/cloudbees/jenkins/GitHubPushTriggerTestCase.java b/src/test/java/com/cloudbees/jenkins/GitHubPushTriggerTestCase.java new file mode 100644 index 000000000..4ac538235 --- /dev/null +++ b/src/test/java/com/cloudbees/jenkins/GitHubPushTriggerTestCase.java @@ -0,0 +1,79 @@ +package com.cloudbees.jenkins; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +import org.jenkinsci.plugins.github.GitHubPlugin; +import org.jenkinsci.plugins.github.config.GitHubPluginConfig; +import org.junit.Rule; +import org.junit.Test; +import org.jvnet.hudson.test.JenkinsRule; + +import hudson.model.FreeStyleProject; +import hudson.plugins.git.GitSCM; +import hudson.scm.SCM; + +/** + * @author Achim Derigs + */ +public final class GitHubPushTriggerTestCase { + + @Rule + public final JenkinsRule jenkins = new JenkinsRule(); + + private static void triggerWebHook(final String repositoryUrl, final String pusherName) throws IOException { + final GitHubPluginConfig configuration = GitHubPlugin.configuration(); + final URL url = configuration.getHookUrl(); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("X-GitHub-Event", "push"); + connection.setDoOutput(true); + connection.connect(); + + final String payload = "payload={\"repository\":{\"url\":\"" + repositoryUrl + "\"},\"pusher\":{\"name\":\"" + pusherName + "\",\"email\":\"\"}}"; + + try (final OutputStream stream = connection.getOutputStream()) { + stream.write(payload.getBytes(UTF_8)); + } + + assertEquals("fails to connect to " + url, HttpURLConnection.HTTP_OK, connection.getResponseCode()); + } + + @Test + public void trigger() throws IOException { + final String repositoryUrl = "https://github.com/kohsuke/foo"; + final SCM scm = new GitSCM(repositoryUrl); + final FreeStyleProject project = jenkins.createFreeStyleProject(); + project.setScm(scm); + + final String expected = System.getProperty("user.name"); + final GitHubPushTrigger trigger = new GitHubPushTrigger(); + project.addTrigger(trigger); + triggerWebHook(repositoryUrl, expected); + + final String actual = trigger.pushBy(); + assertEquals("fails to be triggered by pusher: ", expected, actual); + } + + @Test + public void ignore() throws IOException { + final String repositoryUrl = "https://github.com/kohsuke/foo"; + final SCM scm = new GitSCM(repositoryUrl); + final FreeStyleProject project = jenkins.createFreeStyleProject(); + project.setScm(scm); + + final String pusherName = System.getProperty("user.name"); + final GitHubPushTrigger trigger = new GitHubPushTrigger(pusherName); + project.addTrigger(trigger); + triggerWebHook(repositoryUrl, pusherName); + + final Object object = trigger.pushBy(); + assertNull("fails to ignore pusher: ", object); + } +}