package org.jenkinsci.plugins.github.webhook.subscriber;
import com.cloudbees.jenkins.GitHubPushTrigger;
import com.cloudbees.jenkins.GitHubRepositoryName;
import com.cloudbees.jenkins.GitHubRepositoryNameContributor;
import com.cloudbees.jenkins.GitHubTriggerEvent;
import com.cloudbees.jenkins.GitHubWebHook;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.model.Item;
import hudson.security.ACL;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import jenkins.model.Jenkins;
import org.jenkinsci.plugins.github.extension.GHSubscriberEvent;
import org.jenkinsci.plugins.github.extension.GHEventsSubscriber;
import org.kohsuke.github.GHEvent;
import org.kohsuke.github.GHEventPayload;
import org.kohsuke.github.GitHub;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import static com.google.common.collect.Sets.immutableEnumSet;
import static org.jenkinsci.plugins.github.util.JobInfoHelpers.triggerFrom;
import static org.jenkinsci.plugins.github.util.JobInfoHelpers.withTrigger;
import static org.kohsuke.github.GHEvent.PUSH;
/**
* By default this plugin interested in push events only when job uses {@link GitHubPushTrigger}
*
* @author lanwen (Merkushev Kirill)
* @since 1.12.0
*/
@Extension
@SuppressWarnings("unused")
public class DefaultPushGHEventSubscriber extends GHEventsSubscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPushGHEventSubscriber.class);
/**
* This subscriber is applicable only for job with GHPush trigger
*
* @param project to check for trigger
*
* @return true if project has {@link GitHubPushTrigger}
*/
@Override
protected boolean isApplicable(Item project) {
return withTrigger(GitHubPushTrigger.class).apply(project);
}
/**
* @return set with only push event
*/
@Override
protected Set<GHEvent> events() {
return immutableEnumSet(PUSH);
}
/**
* Calls {@link GitHubPushTrigger} in all projects to handle this hook
*
* @param event only PUSH event
*/
@Override
protected void onEvent(final GHSubscriberEvent event) {
GHEventPayload.Push push;
try {
push = GitHub.offline().parseEventPayload(new StringReader(event.getPayload()), GHEventPayload.Push.class);
} catch (IOException e) {
LOGGER.warn("Received malformed PushEvent: " + event.getPayload(), e);
return;
}
URL repoUrl = push.getRepository().getUrl();
final String pusherName = push.getPusher().getName();
LOGGER.info("Received PushEvent for {} from {}", repoUrl, event.getOrigin());
final GitHubRepositoryName changedRepository = GitHubRepositoryName.create(repoUrl.toExternalForm());
if (changedRepository != null) {
// run in high privilege to see all the projects anonymous users don't see.
// this is safe because when we actually schedule a build, it's a build that can
// happen at some random time anyway.
ACL.impersonate(ACL.SYSTEM, new Runnable() {
@Override
public void run() {
for (Item job : Jenkins.getInstance().getAllItems(Item.class)) {
GitHubPushTrigger trigger = triggerFrom(job, GitHubPushTrigger.class);
if (trigger != null) {
String fullDisplayName = job.getFullDisplayName();
LOGGER.debug("Considering to poke {}", fullDisplayName);
if (GitHubRepositoryNameContributor.parseAssociatedNames(job)
.contains(changedRepository)) {
LOGGER.info("Poked {}", fullDisplayName);
trigger.onPost(GitHubTriggerEvent.create()
.withTimestamp(event.getTimestamp())
.withOrigin(event.getOrigin())
.withTriggeredByUser(pusherName)
.build()
);
} else {
LOGGER.debug("Skipped {} because it doesn't have a matching repository.",
fullDisplayName);
}
}
}
}
});
for (GitHubWebHook.Listener listener : ExtensionList.lookup(GitHubWebHook.Listener.class)) {
listener.onPushRepositoryChanged(pusherName, changedRepository);
}
} else {
LOGGER.warn("Malformed repo url {}", repoUrl);
}
}
}