Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #60 from ikedam/feature/JENKINS-21013_file-from-ma…
…trix-build

[JENKINS-21013] Matrix project support for "Parameters from properties file".
  • Loading branch information
ikedam committed Feb 2, 2014
2 parents fea0e12 + fe091d9 commit 1b68ab5
Show file tree
Hide file tree
Showing 10 changed files with 618 additions and 3 deletions.
Expand Up @@ -12,31 +12,63 @@
import hudson.model.StringParameterValue;
import hudson.model.TaskListener;
import hudson.util.FormValidation;
import hudson.matrix.AxisList;
import hudson.matrix.Combination;
import hudson.matrix.MatrixBuild;
import hudson.matrix.MatrixProject;
import hudson.matrix.MatrixRun;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Nullable;

import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;

public class FileBuildParameters extends AbstractBuildParameters {
private static final Logger LOGGER = Logger.getLogger(FileBuildParameters.class.getName());

private final String propertiesFile;
private final String encoding;
private final boolean failTriggerOnMissing;

/*properties used for a matrix project*/
private final boolean useMatrixChild;
private final String combinatioFilter;
private final boolean onlyExactRuns;

@DataBoundConstructor
public FileBuildParameters(String propertiesFile, String encoding, boolean failTriggerOnMissing) {
public FileBuildParameters(String propertiesFile, String encoding, boolean failTriggerOnMissing, boolean useMatrixBuild, String combinationFilter, boolean onlyExactRuns) {
this.propertiesFile = propertiesFile;
this.encoding = Util.fixEmptyAndTrim(encoding);
this.failTriggerOnMissing = failTriggerOnMissing;
this.useMatrixChild = useMatrixBuild;
if (this.useMatrixChild) {
this.combinatioFilter = combinationFilter;
this.onlyExactRuns = onlyExactRuns;
} else {
this.combinatioFilter = null;
this.onlyExactRuns = false;
}
}

public FileBuildParameters(String propertiesFile, String encoding, boolean failTriggerOnMissing) {
this(propertiesFile, encoding, failTriggerOnMissing, false, null, false);
}

public FileBuildParameters(String propertiesFile, boolean failTriggerOnMissing) {
Expand All @@ -63,6 +95,24 @@ public Action getAction(AbstractBuild<?,?> build, TaskListener listener)
String[] allFiles = Util.tokenize(resolvedPropertiesFile, ",");
List<ParameterValue> values = new ArrayList<ParameterValue>();

// builds to scan.
Collection<? extends AbstractBuild<?,?>> targetBuilds = getTargetBuilds(build);

for (AbstractBuild<?,?> targetBuild: targetBuilds) {
if (!targetBuild.getWorkspace().exists()) {
// This is a case that the workspace is already removed.
LOGGER.log(Level.WARNING, "workspace for {0} is already removed. skip.", targetBuild.getDisplayName());
continue;
}
values.addAll(extractAllValues(targetBuild, listener, allFiles));
}
//Values might be empty, in that case don't return anything.
return values.size() == 0 ? null :new ParametersAction(values);
}

private List<ParameterValue> extractAllValues(AbstractBuild<?,?> build, TaskListener listener, String[] allFiles) throws IOException, InterruptedException, DontTriggerException {
List<ParameterValue> values = new ArrayList<ParameterValue>();
EnvVars env = getEnvironment(build, listener);
for(String file:allFiles) {
FilePath f = build.getWorkspace().child(file);
if (!f.exists()) {
Expand All @@ -85,9 +135,32 @@ public Action getAction(AbstractBuild<?,?> build, TaskListener listener)
entry.getValue().toString()));
}
}
//Values might be empty, in that case don't return anything.
return values.size() == 0 ? null :new ParametersAction(values);
return values;
}

private Collection<? extends AbstractBuild<?, ?>> getTargetBuilds(AbstractBuild<?, ?> build) {
if ((build instanceof MatrixBuild) && isUseMatrixChild()) {
return Collections2.filter(
isOnlyExactRuns()?((MatrixBuild)build).getExactRuns():((MatrixBuild)build).getRuns(),
new Predicate<MatrixRun>() {
public boolean apply(@Nullable MatrixRun run) {
if (run == null) {
return false;
}
if (StringUtils.isBlank(getCombinatioFilter())) {
// no combination filter stands for all children.
return true;
}
Combination c = run.getParent().getCombination();
AxisList axes = run.getParent().getParent().getAxes();

return c.evalGroovyExpression(axes, getCombinatioFilter());
}
}
);
} else {
return Arrays.<AbstractBuild<?,?>>asList(build);
}
}

public String getPropertiesFile() {
Expand All @@ -102,6 +175,18 @@ public boolean getFailTriggerOnMissing() {
return failTriggerOnMissing;
}

public boolean isUseMatrixChild() {
return useMatrixChild;
}

public String getCombinatioFilter() {
return combinatioFilter;
}

public boolean isOnlyExactRuns() {
return onlyExactRuns;
}

@Extension
public static class DescriptorImpl extends Descriptor<AbstractBuildParameters> {
@Override
Expand All @@ -124,6 +209,20 @@ public FormValidation doCheckEncoding(@QueryParameter String encoding) {
}
return FormValidation.ok();
}

/**
* Check whether the configuring model is {@link MatrixProject}. Called from jelly.
*
* Note: Caller should pass it for the model is not bound to
* {@link StaplerRequest#findAncestorObject(Class)}
* when called via hetelo-list.
*
* @param it
* @return true if the target model is {@link MatrixProject}
*/
public boolean isMatrixProject(Object it) {
return (it != null) && (it instanceof MatrixProject);
}
}

}
Expand Up @@ -38,6 +38,7 @@ THE SOFTWARE.
oneEach="true"
items="${instance.configs}"
addCaption="${%Add Parameters}"
capture="configFor"
/>
</f:block>
<f:block>
Expand Down
Expand Up @@ -2,6 +2,7 @@
<f:entry title="${%Build Triggers}" field="configs">
<f:repeatable field="configs" noAddButton="true" minimum="1">
<j:set var="instance" value="${instance.descriptor!=descriptor?null:instance}"/><!-- work around until 1.382 -->
<j:set var="configFor" value="publisher" /> <!-- used for views of AbstractBuildParameters to determine called for builder or publisher -->
<table width="100%">
<st:include class="${descriptor.clazz}" page="config.jelly" />

Expand Down
Expand Up @@ -18,6 +18,7 @@
oneEach="true"
items="${instance.configs}"
addCaption="${%Add Parameters}"
capture="configFor"
/>

</f:block>
Expand Down
Expand Up @@ -6,6 +6,18 @@
<f:entry field="failTriggerOnMissing" title="${%Don't trigger if any files are missing}">
<f:checkbox default="false" />
</f:entry>
<!-- When rendered via Ajax, configFor is available only when Jenkins >= 1.495 -->
<j:if test="${(configFor == null or configFor == 'publisher') and descriptor.isMatrixProject(it)}">
<!-- Here is displayed only for matrix project-->
<f:optionalBlock field="useMatrixChild" title="${%Use files in matrix child builds}">
<f:entry field="combinationFilter" title="Combination Filter">
<f:textbox />
</f:entry>
<f:entry field="onlyExactRuns" title="Only files in exact child builds">
<f:checkbox />
</f:entry>
</f:optionalBlock>
</j:if>
<f:advanced>
<f:entry field="encoding" title="${%File Encoding}">
<f:textbox />
Expand Down
@@ -0,0 +1,6 @@
<div>
Write groovy expression to filter child builds whose files to use.
If not specified, uses all children.

See Combination Filter of Configuration Matrix for details of groovy expression.
</div>
@@ -0,0 +1,4 @@
<div>
Use child builds that is triggered exactly by the parent build.
This affects the behavior when you triggered only some of combinations using plugins, you removed some values from axes, and so on.
</div>
@@ -0,0 +1,4 @@
<div>
Use files in workspaces of child builds (that is, builds for each axes combination).
This works only when used in publishers of multi-configuration projects. Ignored when used in builders.
</div>
Expand Up @@ -2,6 +2,7 @@
<f:entry title="${%Build Triggers}" field="configs">
<f:repeatable field="configs" noAddButton="true" minimum="1">
<j:set var="instance" value="${instance.descriptor!=descriptor?null:instance}"/><!-- work around until 1.382 -->
<j:set var="configFor" value="builder" /> <!-- used for views of AbstractBuildParameters to determine called for builder or publisher -->
<table width="100%">
<st:include class="${descriptor.clazz}" page="config.jelly" />

Expand Down

0 comments on commit 1b68ab5

Please sign in to comment.