Skip to content

Commit

Permalink
[FIXED JENKINS-8590] added option for controlling the firing for
Browse files Browse the repository at this point in the history
MatrixProject.
  • Loading branch information
kohsuke committed Nov 8, 2011
1 parent 9b218b8 commit 906d076
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 25 deletions.
61 changes: 58 additions & 3 deletions src/main/java/hudson/plugins/emailext/ExtendedEmailPublisher.java
Expand Up @@ -3,6 +3,10 @@
import hudson.EnvVars;
import hudson.Extension;
import hudson.Launcher;
import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixRun;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
Expand Down Expand Up @@ -49,7 +53,7 @@
/**
* {@link Publisher} that sends notification e-mail.
*/
public class ExtendedEmailPublisher extends Notifier {
public class ExtendedEmailPublisher extends Notifier implements MatrixAggregatable {

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

Expand Down Expand Up @@ -128,6 +132,8 @@ public static List<EmailTrigger> getTriggersForNonConfiguredInstance() {
*/
public String attachmentsPattern;

private MatrixTriggerMode matrixTriggerMode;

/**
* Get the list of configured email triggers for this project.
*/
Expand Down Expand Up @@ -174,14 +180,29 @@ public boolean getConfigured() {
return isConfigured();
}

public MatrixTriggerMode getMatrixTriggerMode() {
if (matrixTriggerMode ==null) return MatrixTriggerMode.BOTH;
return matrixTriggerMode;
}

public void setMatrixTriggerMode(MatrixTriggerMode matrixTriggerMode) {
this.matrixTriggerMode = matrixTriggerMode;
}

@Override
public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) {
return _perform(build, listener, true);
if (!(build instanceof MatrixRun) || isExecuteOnMatrixNodes()) {
return _perform(build, listener, true);
}
return true;
}

@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
return _perform(build, listener, false);
if (!(build instanceof MatrixRun) || isExecuteOnMatrixNodes()) {
return _perform(build, listener, false);
}
return true;
}

private boolean _perform(AbstractBuild<?, ?> build, BuildListener listener, boolean forPreBuild) {
Expand Down Expand Up @@ -383,6 +404,12 @@ private String getRecipientList(final EmailType type, final AbstractBuild<?, ?>
final String recipientsTransformed = new ContentBuilder().transformText(recipients, this, type, build);
return recipientsTransformed;
}

public boolean isExecuteOnMatrixNodes() {
MatrixTriggerMode mtm = getMatrixTriggerMode();
return MatrixTriggerMode.BOTH == mtm
|| MatrixTriggerMode.ONLY_CONFIGURATIONS == mtm;
}

private MimeBodyPart getContent(final EmailType type, final AbstractBuild<?, ?> build, MimeMessage msg, String charset)
throws MessagingException {
Expand Down Expand Up @@ -440,4 +467,32 @@ public BuildStepDescriptor<Publisher> getDescriptor() {
public static final class DescriptorImpl
extends ExtendedEmailPublisherDescriptor {
}

public MatrixAggregator createAggregator(MatrixBuild matrixbuild,
Launcher launcher, BuildListener buildlistener) {
return new MatrixAggregator(matrixbuild, launcher, buildlistener) {
@Override
public boolean endBuild() throws InterruptedException, IOException {
LOGGER.log(Level.FINER,"end build of " + this.build.getDisplayName());

// Will be run by parent so we check if needed to be executed by parent
if (getMatrixTriggerMode().forParent) {
return ExtendedEmailPublisher.this._perform(this.build, this.listener, false);
}
return true;
}


@Override
public boolean startBuild() throws InterruptedException,IOException {
LOGGER.log(Level.FINER,"end build of " + this.build.getDisplayName());
// Will be run by parent so we check if needed to be executed by parent
if (getMatrixTriggerMode().forParent) {
return ExtendedEmailPublisher.this._perform(this.build, this.listener, true);
}
return true;
}

};
}
}
@@ -1,5 +1,6 @@
package hudson.plugins.emailext;

import hudson.matrix.MatrixProject;
import hudson.model.AbstractProject;
import hudson.model.Hudson;
import hudson.plugins.emailext.plugins.EmailTrigger;
Expand Down Expand Up @@ -36,7 +37,7 @@ public class ExtendedEmailPublisherDescriptor extends BuildStepDescriptor<Publis
* Jenkins's own URL, to put into the e-mail.
*/
private String hudsonUrl;

/**
* If non-null, use SMTP-AUTH
*/
Expand Down Expand Up @@ -249,6 +250,8 @@ public Publisher newInstance(StaplerRequest req, JSONObject formData)
}
}

m.setMatrixTriggerMode(req.bindJSON(MatrixTriggerMode.class,MatrixTriggerMode.class,formData.opt("matrixTriggerMode")));

return m;
}

Expand Down Expand Up @@ -376,4 +379,8 @@ public FormValidation doMaxAttachmentSizeCheck(@QueryParameter final String valu
return FormValidation.error(e.getMessage());
}
}

public boolean isMatrixProject(AbstractProject<?, ?> project) {
return project instanceof MatrixProject;
}
}
27 changes: 27 additions & 0 deletions src/main/java/hudson/plugins/emailext/MatrixTriggerMode.java
@@ -0,0 +1,27 @@
package hudson.plugins.emailext;

import org.jvnet.localizer.Localizable;

/**
* Controls when the e-mail gets sent in case of the matrix project.
*/
public enum MatrixTriggerMode {
ONLY_PARENT(Messages._MatrixTriggerMode_OnlyParent(),true,false),
ONLY_CONFIGURATIONS(Messages._MatrixTriggerMode_OnlyConfigurations(),false,true),
BOTH(Messages._MatrixTriggerMode_Both(),true,true); // traditional default behaviour

private final Localizable description;

public final boolean forParent;
public final boolean forChild;

private MatrixTriggerMode(Localizable description, boolean forParent, boolean forChild) {
this.description = description;
this.forParent = forParent;
this.forChild = forChild;
}

public String getDescription() {
return description.toString();
}
}
Expand Up @@ -30,9 +30,10 @@ public String getHelpText() {
public <P extends AbstractProject<P, B>, B extends AbstractBuild<P, B>> String getContent(AbstractBuild<P, B> build, ExtendedEmailPublisher publisher,
EmailType emailType, Map<String, ?> args) {

// Build can be "building" when the pre-build trigger is used.
// Build can be "building" when the pre-build trigger is used. (and in this case there is not result set yet for the build)
// Reporting "success", "still failing", etc doesn't make sense in this case.
if (build.isBuilding()) {
// When using on matrix build, the build is still in building stage when matrix aggregator end build trigger is fired, though
if ( (build.isBuilding()) && (null == build.getResult())) {
return "Building";
}

Expand Down
Expand Up @@ -113,10 +113,17 @@
<td colspan="2"><div id="${secId}contentTokenHelpConf" class="help" style="display:none">${contentTokenText}</div></td>
<td></td>
</tr>

<!-- Configure advanced properties like per-build-result status email contents,
whether or not to send email to developers who made changes, and whether or
not to send email to the global list of devs-->


<j:if test="${descriptor.isMatrixProject(it)}">
<f:entry title="${%Trigger for matrix projects}" field="matrixTriggerMode">
<f:enum>${it.description}</f:enum>
</f:entry>
</j:if>

<!-- Configure advanced properties like per-build-result status email contents,
whether or not to send email to developers who made changes, and whether or
not to send email to the global list of devs-->
<f:advanced>

<tr>
Expand Down
@@ -0,0 +1,12 @@
<div>
Specifies when to trigger a downstream build for matrix jobs:
<ul>
<li>ONLY_PARENT: trigger only once when parent finishes</li>
<li>ONLY_CONFIGURATIONS: trigger for each configuration</li>
<li>BOTH: combination of the 2 above options</li>
</ul>

Note: that downstream jobs will only be triggered, if they are not already in the build queue.
So, if all your configurations finish within a short timeframe, only one downstream job
might be triggered, even if you have selected ONLY_CONFIGURATIONS or BOTH.
</div>
@@ -1,2 +1,6 @@
ExtendedEmailPublisherDescriptor.DisplayName=Editable Email Notification
ExtendedEmailPublisherDescriptor.AdminAddress=address not configured yet <nobody>
ExtendedEmailPublisherDescriptor.AdminAddress=address not configured yet <nobody>

MatrixTriggerMode.OnlyParent=Trigger only the parent job
MatrixTriggerMode.OnlyConfigurations=Trigger for each configuration
MatrixTriggerMode.Both=Trigger for parent and each configuration
@@ -0,0 +1,116 @@
package hudson.plugins.emailext;

import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;
import hudson.matrix.Axis;
import hudson.matrix.AxisList;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixProject;
import hudson.model.labels.LabelAtom;
import hudson.plugins.emailext.plugins.EmailTrigger;
import hudson.plugins.emailext.plugins.trigger.PreBuildTrigger;
import hudson.slaves.DumbSlave;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.mock_javamail.Mailbox;

public class ExtendedEmailPublisherMatrixTest extends HudsonTestCase{

private ExtendedEmailPublisher publisher;
private MatrixProject project;
private List<DumbSlave> slaves;

public void setUp() throws Exception{
super.setUp();

publisher = new ExtendedEmailPublisher();
publisher.defaultSubject = "%DEFAULT_SUBJECT";
publisher.defaultContent = "%DEFAULT_CONTENT";

project = createMatrixProject();
project.getPublishersList().add( publisher );
slaves = new ArrayList<DumbSlave>();
slaves.add(createOnlineSlave(new LabelAtom("success-slave1")));
slaves.add(createOnlineSlave(new LabelAtom("success-slave2")));
slaves.add(createOnlineSlave(new LabelAtom("success-slave3")));



//build.
}

public void tearDown() throws Exception
{
super.tearDown();
slaves.clear();
Mailbox.clearAll();
}

public void testPreBuildMatrixBuildSendParentOnly() throws Exception{
publisher.setMatrixTriggerMode(MatrixTriggerMode.ONLY_PARENT);
PreBuildTrigger trigger = new PreBuildTrigger();
addEmailType( trigger );
publisher.getConfiguredTriggers().add( trigger );
MatrixBuild build = project.scheduleBuild2(0).get();
assertBuildStatusSuccess(build);


assertThat( "Email should have been triggered, so we should see it in the logs.", build.getLog( 100 ),
hasItems( "Email was triggered for: " + PreBuildTrigger.TRIGGER_NAME ) );
assertEquals( 1, Mailbox.get( "solganik@gmail.com" ).size() );
}

public void testPreBuildMatrixBuildSendSlavesOnly() throws Exception{
addSlaveToProject(0,1,2);

publisher.setMatrixTriggerMode(MatrixTriggerMode.ONLY_CONFIGURATIONS);
PreBuildTrigger trigger = new PreBuildTrigger();
addEmailType( trigger );
publisher.getConfiguredTriggers().add( trigger );


MatrixBuild build = project.scheduleBuild2(0).get();
assertBuildStatusSuccess(build);
assertEquals( 3, Mailbox.get( "solganik@gmail.com" ).size() );
}

public void testPreBuildMatrixBuildSendSlavesAndParent() throws Exception{
addSlaveToProject(0,1);

publisher.setMatrixTriggerMode(MatrixTriggerMode.BOTH);
PreBuildTrigger trigger = new PreBuildTrigger();
addEmailType( trigger );
publisher.getConfiguredTriggers().add( trigger );


MatrixBuild build = project.scheduleBuild2(0).get();
assertBuildStatusSuccess(build);
assertEquals( 3, Mailbox.get( "solganik@gmail.com" ).size() );
}


private void addEmailType( EmailTrigger trigger )
{
trigger.setEmail( new EmailType()
{{
setRecipientList( "solganik@gmail.com" );
setSubject( "Yet another Hudson email" );
setBody( "Boom goes the dynamite." );
}} );
}
private void addSlaveToProject(int ... slaveInxes ) throws IOException{
AxisList list = new AxisList();
List<String> values = new LinkedList<String>();
for (int slaveInx : slaveInxes) {
values.add(slaves.get(slaveInx).getLabelString());
}
list.add(new Axis("label",values));
project.setAxes(list);
}

}
Expand Up @@ -19,8 +19,10 @@
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.MockBuilder;
import org.jvnet.mock_javamail.Mailbox;
import org.kohsuke.stapler.Stapler;

import java.util.List;
import java.util.concurrent.Callable;

import javax.mail.Message;
import javax.mail.internet.MimeBodyPart;
Expand Down Expand Up @@ -375,20 +377,26 @@ public String getUserId() {
public void testNewInstance_shouldGetBasicInformation()
throws Exception
{
JSONObject form = new JSONObject();
form.put( "project_content_type", "default" );
form.put( "recipientlist_recipients", "ashlux@gmail.com" );
form.put( "project_default_subject", "Make millions in Nigeria" );
form.put( "project_default_content", "Give me a $1000 check and I'll mail you back $5000!!!" );
form.put( "project_attachments", "");

publisher = (ExtendedEmailPublisher) ExtendedEmailPublisher.DESCRIPTOR.newInstance( null, form );

assertEquals( "default", publisher.contentType );
assertEquals( "ashlux@gmail.com", publisher.recipientList );
assertEquals( "Make millions in Nigeria", publisher.defaultSubject );
assertEquals( "Give me a $1000 check and I'll mail you back $5000!!!", publisher.defaultContent );
assertEquals( "", publisher.attachmentsPattern);
createWebClient().executeOnServer(new Callable<Object>() {
public Void call() throws Exception {
JSONObject form = new JSONObject();
form.put( "project_content_type", "default" );
form.put( "recipientlist_recipients", "ashlux@gmail.com" );
form.put( "project_default_subject", "Make millions in Nigeria" );
form.put( "project_default_content", "Give me a $1000 check and I'll mail you back $5000!!!" );
form.put( "project_attachments", "");

publisher = (ExtendedEmailPublisher) ExtendedEmailPublisher.DESCRIPTOR.newInstance(Stapler.getCurrentRequest(), form );

assertEquals( "default", publisher.contentType );
assertEquals( "ashlux@gmail.com", publisher.recipientList );
assertEquals( "Make millions in Nigeria", publisher.defaultSubject );
assertEquals( "Give me a $1000 check and I'll mail you back $5000!!!", publisher.defaultContent );
assertEquals( "", publisher.attachmentsPattern);

return null;
}
});
}

private void addEmailType( EmailTrigger trigger )
Expand Down

0 comments on commit 906d076

Please sign in to comment.