Skip to content
Permalink
Browse files
[JENKINS-39355 Follow-up] Adapt to SCM API 2.0 APIs
- Adds CrumbExclusion for notifyCommit hook
- Adds a more efficient notify hook
  • Loading branch information
stephenc committed Dec 16, 2016
1 parent 8ef4e51 commit d0f8f4b930b96fd2da53ae50b0037163eb09ae48
17 pom.xml
@@ -81,12 +81,12 @@
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1.9.4</version>
<version>2.1.10</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>1.4-SNAPSHOT</version>
<version>2.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
@@ -120,6 +120,19 @@
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
<version>2.0.1-SNAPSHOT</version>
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>branch-api</artifactId>
<version>2.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-multibranch</artifactId>
@@ -0,0 +1,103 @@
/*
* The MIT License
*
* Copyright (c) 2016 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

package hudson.plugins.mercurial;

import java.io.Serializable;
import java.net.URI;
import javax.annotation.Nonnull;
import jenkins.scm.api.SCMHeadEvent;

/**
* Payload for a {@link SCMHeadEvent}
*
* @since FIXME
*/
public class MercurialCommitPayload implements Serializable {
@Nonnull
private final URI url;
@Nonnull
private final String branch;
@Nonnull
private final String changesetId;

public MercurialCommitPayload(@Nonnull URI url, @Nonnull String branch, @Nonnull String commitId) {
this.url = url;
this.branch = branch;
this.changesetId = commitId;
}

@Nonnull
public URI getUrl() {
return url;
}

@Nonnull
public String getBranch() {
return branch;
}

@Nonnull
public String getChangesetId() {
return changesetId;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}

MercurialCommitPayload that = (MercurialCommitPayload) o;

if (!url.equals(that.url)) {
return false;
}
if (!branch.equals(that.branch)) {
return false;
}
return changesetId.equals(that.changesetId);
}

@Override
public int hashCode() {
int result = url.hashCode();
result = 31 * result + branch.hashCode();
result = 31 * result + changesetId.hashCode();
return result;
}

@Override
public String toString() {
return "MercurialCommitPayload{" +
"url='" + url+ '\'' +
", branch='" + branch + '\'' +
", commitId='" + changesetId + '\'' +
'}';
}
}
@@ -0,0 +1,95 @@
/*
* The MIT License
*
* Copyright (c) 2016 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/

package hudson.plugins.mercurial;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.scm.SCM;
import java.util.Collections;
import java.util.Map;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMNavigator;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;

/**
* Implementation of {@link SCMHeadEvent} for {@link MercurialSCM} / {@link MercurialSCMSource}.
*
* @since FIXME
*/
public class MercurialSCMHeadEvent extends SCMHeadEvent<MercurialCommitPayload> {
private final MercurialCommitPayload payload;

public MercurialSCMHeadEvent(Type type, MercurialCommitPayload payload) {
super(type, payload);
this.payload = payload;
}

@Override
public boolean isMatch(@NonNull SCMNavigator navigator) {
return false; // because we do not have a Mercurial SCM Navigator
}

@NonNull
@Override
public String getSourceName() {
return null; // because we do not have a Mercurial SCM Navigator
}

@NonNull
@Override
public Map<SCMHead, SCMRevision> heads(@NonNull SCMSource source) {
if (source instanceof MercurialSCMSource) {
MercurialSCMSource hg = (MercurialSCMSource) source;
String repository = hg.getSource();
if (repository != null) {
if (MercurialStatus.looselyMatches(payload.getUrl(), repository)) {
SCMHead head = new SCMHead(getPayload().getBranch());
SCMRevision revision = new MercurialSCMSource.MercurialRevision(
head, getPayload().getChangesetId()
);
return Collections.singletonMap(head, revision);
}
}
}
return Collections.emptyMap();
}

@Override
public boolean isMatch(@NonNull SCM scm) {
if (scm instanceof MercurialSCM) {
MercurialSCM hg = (MercurialSCM) scm;
String repository = hg.getSource();
if (repository != null) {
if (MercurialStatus.looselyMatches(payload.getUrl(), repository)) {
return hg.getRevisionType() == MercurialSCM.RevisionType.BRANCH
&& payload.getBranch().equals(hg.getRevision());
}
}
}
return false;
}
}
@@ -30,6 +30,7 @@
import jenkins.model.Jenkins;
import jenkins.scm.api.SCMFile;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMProbe;
import jenkins.scm.api.SCMProbeStat;
@@ -103,6 +104,7 @@ public boolean isClean() {
@Override
protected void retrieve(@edu.umd.cs.findbugs.annotations.CheckForNull SCMSourceCriteria criteria,
@NonNull SCMHeadObserver observer,
@edu.umd.cs.findbugs.annotations.CheckForNull SCMHeadEvent<?> event,
@NonNull final TaskListener listener) throws IOException, InterruptedException {
MercurialInstallation inst = MercurialSCM.findInstallation(installation);
if (inst == null) {
@@ -236,7 +238,7 @@ public List<Descriptor<RepositoryBrowser<?>>> getBrowserDescriptors() {

}

private static final class MercurialRevision extends SCMRevision {
/*package*/ static final class MercurialRevision extends SCMRevision {
private final String hash;
MercurialRevision(SCMHead branch, String hash) {
super(branch);
@@ -2,14 +2,18 @@

import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import hudson.EnvVars;
import hudson.Extension;
import hudson.model.AbstractModelObject;
import hudson.model.Item;
import hudson.model.UnprotectedRootAction;
import hudson.scm.SCM;
import hudson.security.ACL;
import hudson.triggers.SCMTrigger;
import jenkins.scm.api.SCMEvent;
import jenkins.scm.api.SCMHeadEvent;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.QueryParameter;
@@ -38,6 +42,9 @@
*/
@Extension
public class MercurialStatus extends AbstractModelObject implements UnprotectedRootAction {

public static final String URL_NAME = "mercurial";

public String getDisplayName() {
return Messages.MercurialStatus_mercurial();
}
@@ -51,7 +58,7 @@ public String getIconFileName() {
}

public String getUrlName() {
return "mercurial";
return URL_NAME;
}

static private boolean isUnexpandedEnvVar(String str) {
@@ -73,19 +80,60 @@ static boolean looselyMatches(URI notifyUri, String repository) {
return result;
}

public HttpResponse doNotifyCommit(@QueryParameter(required=true) final String url) throws ServletException, IOException {
/**
* Handles the incoming commit notification. <strong>NOTE:</strong> This handles two types of notification:
* <ul>
* <li>Legacy notification such as from hooks like:
* <pre>
* commit.jenkins = wget -q -O /dev/null &lt;jenkins root&gt;/mercurial/notifyCommit?url=&lt;repository remote url&gt;
* </pre>
* </li>
* <li>Mondern notifications such as from hooks like:
* <pre>
* commit.jenkins = python:&lt;path to hook.py&gt;
* </pre>
* using an in-process hook such as
* <pre>
* import requests
* def commit(ui, repo, node, **kwargs):"
* requests.post('&lt;jenkins root&gt;/mercurial/notifyCommit', data={"url":"&lt;repository remote url&gt;","branch":repo[node].branch(),"changesetId":node})
* pass
* </pre>
* </li>
* </ul>
* When used with a legacy notification, multi-branch jobs will be forced to perform full indexing, whereas when
* used with a modern notification that includes the branch and changesetId then the notification will be processed
* using the SCM API event subsystem resulting in a much more scoped and efficient processing of the event.
*
* @param url the URL of the mercurial repository
* @param branch (optional) branch name of the commit.
* @param changesetId (optional) changesetId of the commit.
* @return the HTTP response
* @throws ServletException if something goes wrong.
* @throws IOException if something goes wrong.
*/
@Restricted(NoExternalUse.class) // Exposed by Stapler, not for direct invocation
public HttpResponse doNotifyCommit(@QueryParameter(required=true) final String url,
@QueryParameter String branch,
@QueryParameter String changesetId) throws ServletException, IOException {
// run in high privilege to see all the projects anonymous users don't see.
// this is safe because we only initiate polling.
SecurityContext securityContext = ACL.impersonate(ACL.SYSTEM);
try {
if (StringUtils.isNotBlank(branch) && StringUtils.isNotBlank(changesetId)) {
// TODO if adding support for
SCMHeadEvent.fireNow(new MercurialSCMHeadEvent(
SCMEvent.Type.UPDATED, new MercurialCommitPayload(new URI(url), branch, changesetId)));
return HttpResponses.ok();
}
return handleNotifyCommit(new URI(url));
} catch ( URISyntaxException ex ) {
throw HttpResponses.error(SC_BAD_REQUEST, ex);
} finally {
SecurityContextHolder.setContext(securityContext);
}
}

private HttpResponse handleNotifyCommit(URI url) throws ServletException, IOException {
final List<Item> projects = Lists.newArrayList();
boolean scmFound = false,
@@ -94,7 +142,8 @@ private HttpResponse handleNotifyCommit(URI url) throws ServletException, IOExce
if (jenkins == null) {
return HttpResponses.error(SC_SERVICE_UNAVAILABLE, "Jenkins instance is not ready");
}


// TODO switch to SCMEvent if the hook contains details of the affected branches
for (Item project : jenkins.getAllItems()) {
SCMTriggerItem scmTriggerItem = SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project);
if (scmTriggerItem == null) {
@@ -181,4 +230,5 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
}

private static final Logger LOGGER = Logger.getLogger(MercurialStatus.class.getName());

}

0 comments on commit d0f8f4b

Please sign in to comment.