Skip to content
Permalink
Browse files

Merge pull request #2 from rseguy/master

[FIXED JENKINS-9217 JENKINS-9391]
  • Loading branch information...
rseguy committed May 23, 2011
2 parents d22c738 + ba2c25d commit 418d072595528ebae55e6f4dcdf7bca32972dc18
@@ -1,5 +1,31 @@
/*
* The MIT License
*
* Copyright (c) 2010-2011, InfraDNA, Inc., Manufacture Francaise des Pneumatiques Michelin,
* Romain Seguy
*
* 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.parameterizedtrigger;

import org.apache.commons.lang.StringUtils;
import hudson.Extension;
import hudson.model.Describable;
import hudson.model.Descriptor;
@@ -18,28 +44,50 @@
* @author Kohsuke Kawaguchi
*/
public class BlockingBehaviour implements Describable<BlockingBehaviour> {
public final Result buildStepFailureThreshold;
public final Result unstableThreshold;
public final Result failureThreshold;

@DataBoundConstructor
public BlockingBehaviour(String unstableThreshold, String failureThreshold) {
public BlockingBehaviour(String buildStepFailureThreshold, String unstableThreshold, String failureThreshold) {
this.buildStepFailureThreshold = parse(buildStepFailureThreshold);
this.unstableThreshold = parse(unstableThreshold);
this.failureThreshold = parse(failureThreshold);
}

private Result parse(String t) {
return t.equals("never") ? null : Result.fromString(t);
if(StringUtils.isBlank(t) || "never".equals(t)) {
return null;
}
return Result.fromString(t);
}

public BlockingBehaviour(Result unstableThreshold, Result failureThreshold) {
public BlockingBehaviour(Result buildStepFailureThreshold, Result unstableThreshold, Result failureThreshold) {
this.buildStepFailureThreshold = buildStepFailureThreshold;
this.unstableThreshold = unstableThreshold;
this.failureThreshold = failureThreshold;
}

/**
* Maps the result of a triggered build to the result of the triggering build step.
*
* @param r the {@link Result} of the triggered build to map
* @return {@code false} if the triggering build step has to fail, {@code true} otherwise
*/
public boolean mapBuildStepResult(Result r) {
if (buildStepFailureThreshold!=null && r.isWorseOrEqualTo(buildStepFailureThreshold)) {
return false;
}
return true;
}

/**
* Maps the result of the triggered build to the result of the triggering build.
* Maps the result of a triggered build to the result of the triggering build.
*
* @param r the {@link Result} of the triggered build to map
* @return the result of the triggering build
*/
public Result mapResult(Result r) {
public Result mapBuildResult(Result r) {
if (failureThreshold!=null && r.isWorseOrEqualTo(failureThreshold)) return FAILURE;
if (unstableThreshold!=null && r.isWorseOrEqualTo(unstableThreshold)) return UNSTABLE;
return SUCCESS;
@@ -69,7 +69,7 @@ public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) {
if (!canDeclare(owner)) return;

for (BuildTriggerConfig config : configs)
for (AbstractProject project : config.getProjectList())
for (AbstractProject project : config.getProjectList(null))
ParameterizedDependency.add(owner, project, config, graph);
}

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

import hudson.EnvVars;
import static hudson.Util.fixEmpty;
import hudson.Extension;
import hudson.Launcher;
@@ -31,6 +32,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.StringTokenizer;
import java.util.concurrent.Future;

public class BuildTriggerConfig implements Describable<BuildTriggerConfig> {
@@ -41,6 +43,10 @@
private final ResultCondition condition;
private boolean triggerWithNoParameters;

// the list of projects to build is computed in getProjectList() ; this method
// is actually invoked twice (when in a build step), so let's cache its result
private transient List<AbstractProject> projectList;

@DataBoundConstructor
public BuildTriggerConfig(String projects, ResultCondition condition,
boolean triggerWithNoParameters, List<AbstractBuildParameters> configs) {
@@ -70,10 +76,26 @@ public ResultCondition getCondition() {
public boolean getTriggerWithNoParameters() {
return triggerWithNoParameters;
}

public List<AbstractProject> getProjectList() {
List<AbstractProject> projectList = new ArrayList<AbstractProject>();
projectList.addAll(Items.fromNameList(projects, AbstractProject.class));

/**
* @param env Environment variables from which to expand project names; Might be {@code null}.
*/
public List<AbstractProject> getProjectList(EnvVars env) {
if(projectList == null) {
projectList = new ArrayList<AbstractProject>();

// expand variables if applicable
StringBuilder projectNames = new StringBuilder();
StringTokenizer tokens = new StringTokenizer(projects,",");
while(tokens.hasMoreTokens()) {
if(projectNames.length() > 0) {
projectNames.append(',');
}
projectNames.append(env != null ? env.expand(tokens.nextToken().trim()) : tokens.nextToken().trim());
}

projectList.addAll(Items.fromNameList(projectNames.toString(), AbstractProject.class));
}
return projectList;
}

@@ -141,12 +163,15 @@ private static ParametersAction getDefaultParameters(AbstractProject<?,?> projec
*/
public List<Future<AbstractBuild>> perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException, IOException {
try {
EnvVars env = build.getEnvironment(listener);
env.overrideAll(build.getBuildVariables());

try {
if (condition.isMet(build.getResult())) {
List<Action> actions = getBaseActions(build, listener);

List<Future<AbstractBuild>> futures = new ArrayList<Future<AbstractBuild>>();
for (AbstractProject project : getProjectList()) {
for (AbstractProject project : getProjectList(env)) {
List<Action> list = getBuildActions(actions, project);

futures.add(schedule(build, project, list));
@@ -1,6 +1,32 @@
/*
* The MIT License
*
* Copyright (c) 2010-2011, InfraDNA, Inc., Manufacture Francaise des Pneumatiques Michelin,
* Romain Seguy
*
* 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.parameterizedtrigger;

import hudson.AbortException;
import hudson.EnvVars;
import hudson.Extension;
import hudson.Launcher;
import hudson.Util;
@@ -54,35 +80,50 @@ public BuildStepMonitor getRequiredMonitorService() {
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException, IOException {
EnvVars env = build.getEnvironment(listener);
env.overrideAll(build.getBuildVariables());

Map<BlockableBuildTriggerConfig,List<Future<AbstractBuild>>> futures = new HashMap<BlockableBuildTriggerConfig, List<Future<AbstractBuild>>>();
for (BlockableBuildTriggerConfig config : configs) {
futures.put(config,config.perform(build, launcher, listener));
futures.put(config, config.perform(build, launcher, listener));
}

boolean buildStepResult = true;

try {
for (Entry<BlockableBuildTriggerConfig, List<Future<AbstractBuild>>> e : futures.entrySet()) {
int n=0;
if(!e.getKey().getProjectList().isEmpty()){
AbstractProject p = e.getKey().getProjectList().get(n);
BlockableBuildTriggerConfig config = e.getKey();
List<AbstractProject> projectList = config.getProjectList(env);

if(!projectList.isEmpty()){
AbstractProject p = projectList.get(n);
for (Future<AbstractBuild> f : e.getValue()) {
try {
listener.getLogger().println("Waiting for the completion of "+p.getFullDisplayName());
AbstractBuild b = f.get();
listener.getLogger().println(b.getFullDisplayName()+" completed. Result was "+b.getResult());
build.setResult(e.getKey().getBlock().mapResult(b.getResult()));

if(buildStepResult && config.getBlock().mapBuildStepResult(b.getResult())) {
build.setResult(config.getBlock().mapBuildResult(b.getResult()));
}
else {
buildStepResult = false;
}
} catch (CancellationException x) {
throw new AbortException(p.getFullDisplayName() +" aborted.");
}
n++;
}
}else{
} else {
throw new AbortException("Build aborted. No projects to trigger. Check your configuration!");
}
}
} catch (ExecutionException e) {
throw new IOException2(e); // can't happen, I think.
}

return true;
return buildStepResult;
}

@Extension
@@ -0,0 +1,44 @@
<!--
- The MIT License
-
- Copyright (c) 2011, Manufacture Francaise des Pneumatiques Michelin, Romain Seguy
-
- 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.
-->

<div>
When activating this option, triggered builds will be run synchronously. The
result of the current build step or the current build can then be controlled
using the three following options:<br/><ul>
<li><b>Fail this build step if the triggered build is worse or equal to</b>:
This option takes precedence over the two following ones if its value is not
<b>never</b>. In that case, the result of the current build step (that is,
success or failure) is based on the result of the triggered builds. If the
condition defined for at least one of the triggered build is met, then the
build step will be considered as failed.</li>
<li><b>Mark this build as failure if the triggered build is worse or equal to</b>:
This option takes precedence over the next one if its value is not set to
<b>never</b>. In that case, the current build will be considered as failed
based on the result of the triggered builds and the value of the option.</li>
<li><b>Mark this build as unstable if the triggered build is worse or equal to</b>:
This option is taken into account if its value is not <b>never</b>. In that
case, the build will be considered as unstable based on the result of the
triggered builds and the value of this option.</li>
</ul>
</div>
@@ -1,7 +1,8 @@
<!--
The MIT License
Copyright (c) 2010, InfraDNA, Inc.
Copyright (c) 2010-2011, InfraDNA, Inc., Manufacture Francaise des Pneumatiques Michelin,
Romain Seguy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -22,7 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="${%Mark this build as unstable if the triggered build is worse or equal to}" field="unstableThreshold">
<f:entry title="${%Fail this build step if the triggered build is worse or equal to}" field="buildStepFailureThreshold">
<select class="setting-input" name="${field}">
<f:option value="never">${%never}</f:option>
<j:forEach var="it" items="${descriptor.allResults}">
@@ -43,4 +44,16 @@ THE SOFTWARE.
</j:forEach>
</select>
</f:entry>

<f:entry title="${%Mark this build as unstable if the triggered build is worse or equal to}" field="unstableThreshold">
<select class="setting-input" name="${field}">
<f:option value="never">${%never}</f:option>
<j:forEach var="it" items="${descriptor.allResults}">
<f:option value="${it}" selected="${it==instance[field]}">
${it}
</f:option>
</j:forEach>
</select>
</f:entry>

</j:jelly>

0 comments on commit 418d072

Please sign in to comment.
You can’t perform that action at this time.