Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for configuring when matrix projects notify. #547

Merged
merged 7 commits into from
Apr 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>3.33</version>
<version>3.42</version>
</parent>

<artifactId>slack</artifactId>
Expand Down Expand Up @@ -212,7 +212,6 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>matrix-project</artifactId>
<version>1.7.1</version>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/jenkins/plugins/slack/ActiveNotifier.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jenkins.plugins.slack;

import hudson.Util;
import hudson.matrix.MatrixRun;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.Cause;
Expand Down Expand Up @@ -51,6 +52,9 @@ public void deleted(AbstractBuild r) {
}

public void started(AbstractBuild build) {
if (skipOnMatrixChildren(build)) {
return;
}
String key = BuildKey.format(build);

CauseAction causeAction = build.getAction(CauseAction.class);
Expand Down Expand Up @@ -98,6 +102,9 @@ private void notifyStart(AbstractBuild build, String message) {
}

public void finalized(AbstractBuild r) {
if (skipOnMatrixChildren(r)) {
return;
}
AbstractProject<?, ?> project = r.getProject();
Result result = r.getResult();
AbstractBuild<?, ?> previousBuild = project.getLastBuild();
Expand All @@ -118,6 +125,9 @@ public void finalized(AbstractBuild r) {
}

public void completed(AbstractBuild r) {
if (skipOnMatrixChildren(r)) {
return;
}
String key = BuildKey.format(r);
AbstractProject<?, ?> project = r.getProject();
AbstractBuild<?, ?> previousBuild = project.getLastBuild();
Expand All @@ -143,6 +153,10 @@ public void completed(AbstractBuild r) {
}
}

private boolean skipOnMatrixChildren(AbstractBuild build) {
return build instanceof MatrixRun && !notifier.getMatrixTriggerMode().forChild;
}

private boolean moreTestFailuresThanPreviousBuild(AbstractBuild currentBuild, AbstractBuild<?, ?> previousBuild) {
if (getTestResult(currentBuild) != null && getTestResult(previousBuild) != null) {
if (getTestResult(currentBuild).getFailCount() > getTestResult(previousBuild).getFailCount())
Expand Down
62 changes: 59 additions & 3 deletions src/main/java/jenkins/plugins/slack/SlackNotifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import hudson.Extension;
import hudson.Launcher;
import hudson.Util;
import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixProject;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
Expand All @@ -29,6 +33,7 @@
import jenkins.plugins.slack.logging.BuildAwareLogger;
import jenkins.plugins.slack.logging.BuildKey;
import jenkins.plugins.slack.logging.SlackNotificationsLogger;
import jenkins.plugins.slack.matrix.MatrixTriggerMode;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.Symbol;
Expand All @@ -43,7 +48,7 @@

import static com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials;

public class SlackNotifier extends Notifier {
public class SlackNotifier extends Notifier implements MatrixAggregatable {

private static final Logger logger = Logger.getLogger(SlackNotifier.class.getName());

Expand All @@ -65,6 +70,7 @@ public class SlackNotifier extends Notifier {
private boolean notifyRepeatedFailure;
private boolean includeTestSummary;
private boolean includeFailedTests;
private MatrixTriggerMode matrixTriggerMode = MatrixTriggerMode.ONLY_CONFIGURATIONS;
private CommitInfoChoice commitInfoChoice;
private boolean includeCustomMessage;
private String customMessage;
Expand Down Expand Up @@ -165,6 +171,10 @@ public CommitInfoChoice getCommitInfoChoice() {
return commitInfoChoice;
}

public MatrixTriggerMode getMatrixTriggerMode() {
return matrixTriggerMode;
}

public boolean getNotifyAborted() {
return notifyAborted;
}
Expand Down Expand Up @@ -249,6 +259,11 @@ public void setCommitInfoChoice(CommitInfoChoice commitInfoChoice) {
this.commitInfoChoice = commitInfoChoice;
}

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

@DataBoundSetter
public void setNotifyAborted(boolean notifyAborted) {
this.notifyAborted = notifyAborted;
Expand Down Expand Up @@ -343,17 +358,33 @@ public SlackNotifier(final String baseUrl, final String teamDomain, final String
this(
baseUrl, teamDomain, authToken, botUser, room, tokenCredentialId, sendAs, startNotification,
notifyAborted, notifyFailure, notifyNotBuilt, notifySuccess, notifyUnstable, notifyRegression,
notifyBackToNormal, notifyRepeatedFailure, includeTestSummary, includeFailedTests, commitInfoChoice,
includeCustomMessage, customMessage, null, null, null, null, null
notifyBackToNormal, notifyRepeatedFailure, includeTestSummary, includeFailedTests,
commitInfoChoice, includeCustomMessage, customMessage, null, null, null, null, null
);
}

@Deprecated
public SlackNotifier(final String baseUrl, final String teamDomain, final String authToken, final boolean botUser, final String room, final String tokenCredentialId,
final String sendAs, final boolean startNotification, final boolean notifyAborted, final boolean notifyFailure,
final boolean notifyNotBuilt, final boolean notifySuccess, final boolean notifyUnstable, final boolean notifyRegression, final boolean notifyBackToNormal,
final boolean notifyRepeatedFailure, final boolean includeTestSummary, final boolean includeFailedTests,
CommitInfoChoice commitInfoChoice, boolean includeCustomMessage, String customMessage, String customMessageSuccess,
String customMessageAborted, String customMessageNotBuilt, String customMessageUnstable, String customMessageFailure) {
this(
baseUrl, teamDomain, authToken, botUser, room, tokenCredentialId, sendAs, startNotification,
notifyAborted, notifyFailure, notifyNotBuilt, notifySuccess, notifyUnstable, notifyRegression,
notifyBackToNormal, notifyRepeatedFailure, includeTestSummary, includeFailedTests, MatrixTriggerMode.ONLY_CONFIGURATIONS,
commitInfoChoice, includeCustomMessage, customMessage, customMessageSuccess, customMessageAborted, customMessageNotBuilt,
customMessageUnstable, customMessageFailure
);
}

public SlackNotifier(final String baseUrl, final String teamDomain, final String authToken, final boolean botUser, final String room, final String tokenCredentialId,
final String sendAs, final boolean startNotification, final boolean notifyAborted, final boolean notifyFailure,
final boolean notifyNotBuilt, final boolean notifySuccess, final boolean notifyUnstable, final boolean notifyRegression, final boolean notifyBackToNormal,
final boolean notifyRepeatedFailure, final boolean includeTestSummary, final boolean includeFailedTests, MatrixTriggerMode matrixTriggerMode,
CommitInfoChoice commitInfoChoice, boolean includeCustomMessage, String customMessage, String customMessageSuccess,
String customMessageAborted, String customMessageNotBuilt, String customMessageUnstable, String customMessageFailure) {
super();
this.baseUrl = baseUrl;
if(this.baseUrl != null && !this.baseUrl.isEmpty() && !this.baseUrl.endsWith("/")) {
Expand All @@ -376,6 +407,7 @@ public SlackNotifier(final String baseUrl, final String teamDomain, final String
this.notifyRepeatedFailure = notifyRepeatedFailure;
this.includeTestSummary = includeTestSummary;
this.includeFailedTests = includeFailedTests;
this.matrixTriggerMode = matrixTriggerMode;
this.commitInfoChoice = commitInfoChoice;
this.includeCustomMessage = includeCustomMessage;
if (includeCustomMessage) {
Expand Down Expand Up @@ -470,6 +502,26 @@ public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) {
return super.prebuild(build, listener);
}

public MatrixAggregator createAggregator(MatrixBuild matrixBuild, Launcher launcher, BuildListener buildListener) {
return new MatrixAggregator(matrixBuild, launcher, buildListener) {
@Override
public boolean startBuild() {
if (getMatrixTriggerMode().forParent) {
return SlackNotifier.this.perform(this.build, this.launcher, this.listener);
}
return true;
}

@Override
public boolean endBuild() {
if (getMatrixTriggerMode().forParent) {
return SlackNotifier.this.perform(this.build, this.launcher, this.listener);
}
return true;
}
};
}

private Function<AbstractBuild<?, ?>, SlackService> slackFactory(BuildListener listener) {
return b -> newSlackService(b, listener);
}
Expand Down Expand Up @@ -631,6 +683,10 @@ SlackService getSlackService(final String baseUrl, final String teamDomain, fina
}
}

public boolean isMatrixProject(AbstractProject<?, ?> project) {
return project instanceof MatrixProject;
}

@Nonnull
@Override
public String getDisplayName() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private StringCredentials addCredentialIfNotPresent(@Nonnull String token) {
StringCredentials credentials = new StringCredentialsImpl(
CredentialsScope.GLOBAL,
UUID.randomUUID().toString(),
Messages.MigratedCredentialDescription(),
Messages.migratedCredentialDescription(),
Secret.fromString(token)
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Borrowed from https://github.com/jenkinsci/hipchat-plugin
*/
package jenkins.plugins.slack.matrix;

import jenkins.plugins.slack.Messages;
import org.jvnet.localizer.Localizable;

/**
* Controls when the slack notification 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);

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();
}
}
20 changes: 10 additions & 10 deletions src/main/java/jenkins/plugins/slack/workflow/SlackSendStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public String getFunctionName() {
@Nonnull
@Override
public String getDisplayName() {
return Messages.SlackSendStepDisplayName();
return Messages.slackSendStepDisplayName();
}

public ListBoxModel doFillTokenCredentialIdItems(@AncestorInPath Item item) {
Expand Down Expand Up @@ -250,7 +250,7 @@ protected SlackResponse run() throws Exception {
TaskListener listener = getContext().get(TaskListener.class);
Objects.requireNonNull(listener, "Listener is mandatory here");

listener.getLogger().println(Messages.SlackSendStepValues(
listener.getLogger().println(Messages.slackSendStepValues(
defaultIfEmpty(baseUrl), defaultIfEmpty(teamDomain), channel, defaultIfEmpty(color), botUser,
defaultIfEmpty(tokenCredentialId))
);
Expand All @@ -259,7 +259,7 @@ protected SlackResponse run() throws Exception {
populatedToken = CredentialsObtainer.getTokenToUse(tokenCredentialId, item, token);
} catch (IllegalArgumentException e) {
listener.error(Messages
.NotificationFailedWithException(e));
.notificationFailedWithException(e));
return null;
}

Expand All @@ -281,7 +281,7 @@ protected SlackResponse run() throws Exception {
publishSuccess = slackService.publish(step.message, color);
} else {
listener.error(Messages
.NotificationFailedWithException(new IllegalArgumentException("No message or attachments provided")));
.notificationFailedWithException(new IllegalArgumentException("No message or attachments provided")));
return null;
}
SlackResponse response = null;
Expand All @@ -292,7 +292,7 @@ protected SlackResponse run() throws Exception {
org.json.JSONObject result = new org.json.JSONObject(responseString);
response = new SlackResponse(result);
} catch (org.json.JSONException ex) {
listener.error(Messages.FailedToParseSlackResponse(responseString));
listener.error(Messages.failedToParseSlackResponse(responseString));
if (step.failOnError) {
throw ex;
}
Expand All @@ -301,9 +301,9 @@ protected SlackResponse run() throws Exception {
return new SlackResponse();
}
} else if (step.failOnError) {
throw new AbortException(Messages.NotificationFailed());
throw new AbortException(Messages.notificationFailed());
} else {
listener.error(Messages.NotificationFailed());
listener.error(Messages.notificationFailed());
}
return response;
}
Expand All @@ -322,11 +322,11 @@ JSONArray getAttachmentsAsJSONArray() throws Exception {
try {
json = jsonSlurper.parseText(jsonString);
} catch (JSONException e) {
listener.error(Messages.NotificationFailedWithException(e));
listener.error(Messages.notificationFailedWithException(e));
return null;
}
if (!(json instanceof JSONArray)) {
listener.error(Messages.NotificationFailedWithException(new IllegalArgumentException("Attachments must be JSONArray")));
listener.error(Messages.notificationFailedWithException(new IllegalArgumentException("Attachments must be JSONArray")));
return null;
}
return (JSONArray) json;
Expand Down Expand Up @@ -359,7 +359,7 @@ private Item getItemForCredentials() {
}

private String defaultIfEmpty(String value) {
return Util.fixEmpty(value) != null ? value : Messages.SlackSendStepValuesEmptyMessage();
return Util.fixEmpty(value) != null ? value : Messages.slackSendStepValuesEmptyMessage();
}

//streamline unit testing
Expand Down
19 changes: 12 additions & 7 deletions src/main/resources/jenkins/plugins/slack/Messages.properties
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
# Localization for config pages
SlackSendStepDisplayName=Send Slack Message
slackSendStepDisplayName=Send Slack Message

# Messages to display in the build logs
NotificationFailed=Slack notification failed. See Jenkins logs for details.
NotificationFailedWithException=Slack notification failed with exception: {0}
SlackSendStepValues=Slack Send Pipeline step running, values are - baseUrl: {0}, teamDomain: {1}, channel: {2}, color: {3}, botUser: {4}, tokenCredentialId: {5}
SlackSendStepValuesEmptyMessage=<empty>
FailedToParseSlackResponse=Could not parse response from slack, potentially because of invalid configuration (botUser: true and baseUrl set), response: {0}
notificationFailed=Slack notification failed. See Jenkins logs for details.
notificationFailedWithException=Slack notification failed with exception: {0}
slackSendStepValues=Slack Send Pipeline step running, values are - baseUrl: {0}, teamDomain: {1}, channel: {2}, color: {3}, botUser: {4}, tokenCredentialId: {5}
slackSendStepValuesEmptyMessage=<empty>
failedToParseSlackResponse=Could not parse response from slack, potentially because of invalid configuration (botUser: true and baseUrl set), response: {0}

# Migrated credential description
MigratedCredentialDescription=Migrated slack token
migratedCredentialDescription=Migrated slack token

# Options for selecting when Matrix builds will notify
MatrixTriggerMode.OnlyParent=Trigger only the parent job
MatrixTriggerMode.OnlyConfigurations=Trigger for each configuration
MatrixTriggerMode.Both=Trigger for parent and each configuration
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
<f:checkbox field="notifyBackToNormal" />
</f:entry>

<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>

<f:advanced>
<f:entry title="Include Test Summary">
<f:checkbox field="includeTestSummary" />
Expand Down
8 changes: 8 additions & 0 deletions src/main/webapp/help-matrixTriggerMode.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div>
Specifies when to trigger notifications 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>
</div>
Loading