diff --git a/src/main/java/hudson/plugins/parameterizedtrigger/BuildTrigger.java b/src/main/java/hudson/plugins/parameterizedtrigger/BuildTrigger.java index 38e93186..2a2d8c74 100644 --- a/src/main/java/hudson/plugins/parameterizedtrigger/BuildTrigger.java +++ b/src/main/java/hudson/plugins/parameterizedtrigger/BuildTrigger.java @@ -12,6 +12,8 @@ import hudson.model.BuildListener; import hudson.model.DependecyDeclarer; import hudson.model.DependencyGraph; +import hudson.model.Item; +import hudson.model.ItemGroup; import hudson.model.Job; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; @@ -159,7 +161,19 @@ public void buildDependencyGraph(AbstractProject owner, DependencyGraph graph) { for (BuildTriggerConfig config : configs) { List<AbstractProject> projectList = config.getProjectList(owner.getParent(), null); for (AbstractProject project : projectList) { - ParameterizedDependency.add(owner, project, config, graph); + if (config.isTriggerFromChildProjects() && owner instanceof ItemGroup) { + ItemGroup<Item> parent = (ItemGroup) owner; + for (Item item : parent.getItems()) { + if(item instanceof AbstractProject){ + AbstractProject child = (AbstractProject) item; + ParameterizedDependency.add(child, project, config, graph); + } + } + } + else{ + ParameterizedDependency.add(owner, project, config, graph); + + } } } } diff --git a/src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java b/src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java index 47a2e589..ce4fb09a 100644 --- a/src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java +++ b/src/main/java/hudson/plugins/parameterizedtrigger/BuildTriggerConfig.java @@ -39,6 +39,8 @@ import jenkins.security.QueueItemAuthenticatorConfiguration; import org.acegisecurity.Authentication; import org.apache.commons.lang.StringUtils; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; @@ -66,17 +68,30 @@ public class BuildTriggerConfig implements Describable<BuildTriggerConfig> { private String projects; private final ResultCondition condition; private boolean triggerWithNoParameters; + private boolean triggerFromChildProjects; - public BuildTriggerConfig(String projects, ResultCondition condition, - boolean triggerWithNoParameters, List<AbstractBuildParameterFactory> configFactories, List<AbstractBuildParameters> configs) { + public BuildTriggerConfig(String projects, ResultCondition condition, boolean triggerWithNoParameters, + List<AbstractBuildParameterFactory> configFactories, List<AbstractBuildParameters> configs, boolean triggerFromChildProjects) { this.projects = projects; this.condition = condition; this.triggerWithNoParameters = triggerWithNoParameters; this.configFactories = configFactories; this.configs = Util.fixNull(configs); + this.triggerFromChildProjects = triggerFromChildProjects; + } + + @Deprecated + public BuildTriggerConfig(String projects, ResultCondition condition, + boolean triggerWithNoParameters, List<AbstractBuildParameterFactory> configFactories, List<AbstractBuildParameters> configs) { + this(projects, condition, triggerWithNoParameters, configFactories, configs, false); } @DataBoundConstructor + public BuildTriggerConfig(String projects, ResultCondition condition, + boolean triggerWithNoParameters, List<AbstractBuildParameters> configs, boolean triggerFromChildProjects) { + this(projects, condition, triggerWithNoParameters, null, configs, triggerFromChildProjects); + } + public BuildTriggerConfig(String projects, ResultCondition condition, boolean triggerWithNoParameters, List<AbstractBuildParameters> configs) { this(projects, condition, triggerWithNoParameters, null, configs); @@ -116,6 +131,10 @@ public ResultCondition getCondition() { public boolean getTriggerWithNoParameters() { return triggerWithNoParameters; } + + public boolean isTriggerFromChildProjects(){ + return triggerFromChildProjects; + } /** * @deprecated @@ -631,6 +650,11 @@ public List<Descriptor<AbstractBuildParameterFactory>> getBuilderConfigFactoryDe Descriptor<AbstractBuildParameterFactory>>getDescriptorList(AbstractBuildParameterFactory.class); } + @Restricted(DoNotUse.class) + public boolean isItemGroup(AbstractProject project){ + return project instanceof ItemGroup; + } + /** * Form validation method. * diff --git a/src/main/resources/hudson/plugins/parameterizedtrigger/BuildTriggerConfig/config.jelly b/src/main/resources/hudson/plugins/parameterizedtrigger/BuildTriggerConfig/config.jelly index 2572b6a8..d7f81023 100644 --- a/src/main/resources/hudson/plugins/parameterizedtrigger/BuildTriggerConfig/config.jelly +++ b/src/main/resources/hudson/plugins/parameterizedtrigger/BuildTriggerConfig/config.jelly @@ -9,7 +9,11 @@ <f:entry title="${%Trigger build without parameters}" field="triggerWithNoParameters" > <f:checkbox checked="${instance.triggerWithNoParameters}"/> </f:entry> - + <j:if test="${descriptor.isItemGroup(it)}"> + <f:entry title="${%Trigger build from child projects}" field="triggerFromChildProjects" > + <f:checkbox checked="${instance.isTriggerFromChildProjects()}"/> + </f:entry> + </j:if> <f:block> <f:hetero-list name="configs" hasHeader="true" descriptors="${descriptor.getBuilderConfigDescriptors()}" diff --git a/src/test/java/hudson/plugins/parameterizedtrigger/test/BuildTriggerTest.java b/src/test/java/hudson/plugins/parameterizedtrigger/test/BuildTriggerTest.java new file mode 100644 index 00000000..f9020be7 --- /dev/null +++ b/src/test/java/hudson/plugins/parameterizedtrigger/test/BuildTriggerTest.java @@ -0,0 +1,143 @@ +package hudson.plugins.parameterizedtrigger.test; + +import com.google.common.collect.Lists; +import hudson.AbortException; +import hudson.FilePath; +import hudson.Launcher; +import hudson.matrix.AxisList; +import hudson.matrix.DefaultMatrixExecutionStrategyImpl; +import hudson.matrix.MatrixConfiguration; +import hudson.matrix.MatrixConfigurationSorter; +import hudson.matrix.MatrixProject; +import hudson.matrix.TextAxis; +import hudson.model.AbstractBuild; +import hudson.model.BuildListener; +import hudson.model.Cause; +import hudson.model.FreeStyleBuild; +import hudson.model.FreeStyleProject; +import hudson.model.ParameterDefinition; +import hudson.model.ParametersDefinitionProperty; +import hudson.model.StringParameterDefinition; +import hudson.plugins.parameterizedtrigger.AbstractBuildParameters; +import hudson.plugins.parameterizedtrigger.BuildTrigger; +import hudson.plugins.parameterizedtrigger.BuildTriggerConfig; +import hudson.plugins.parameterizedtrigger.CurrentBuildParameters; +import hudson.plugins.parameterizedtrigger.FileBuildParameters; +import hudson.plugins.parameterizedtrigger.ResultCondition; +import hudson.util.FormValidation; +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import static org.junit.Assert.*; +import org.junit.Rule; +import org.junit.Test; +import hudson.tasks.Builder; +import org.jvnet.hudson.test.JenkinsRule; + +/** + * + * @author Lucie Votypkova + */ +public class BuildTriggerTest { + + @Rule + public JenkinsRule r = new JenkinsRule(); + + @Test + public void testParentProjectTrigger() throws Exception{ + FreeStyleProject downstream = r.createFreeStyleProject("downstream"); + MatrixProject upstream = r.createProject(MatrixProject.class, "upstream"); + List<ParameterDefinition> definition = new ArrayList<ParameterDefinition>(); + definition.add(new StringParameterDefinition("parameter","parameter-value")); + upstream.addProperty(new ParametersDefinitionProperty(definition)); + AxisList axes = new AxisList(); + axes.add(new TextAxis("textAxis","one","two")); + upstream.setAxes(axes); + List<AbstractBuildParameters> params = new ArrayList<AbstractBuildParameters>(); + params.add(new CurrentBuildParameters()); + BuildTrigger triggerBuilder = new BuildTrigger(new BuildTriggerConfig("downstream", ResultCondition.SUCCESS, false, null, params, false)); + upstream.getPublishersList().add(triggerBuilder); + r.buildAndAssertSuccess(upstream); + r.waitUntilNoActivity(); + FreeStyleBuild downstreamBuild = downstream.getLastBuild(); + assertNotNull("Downstream job should be triggered", downstreamBuild); + String project = downstreamBuild.getCause(Cause.UpstreamCause.class).getUpstreamProject(); + assertEquals("Build should be triggered by matrix project.", "upstream", project); + } + + @Test + public void testChildProjectsTrigger() throws Exception{ + MatrixProject upstream = r.createProject(MatrixProject.class, "upstream"); + FreeStyleProject downstream = r.createFreeStyleProject("downstream"); + + List<ParameterDefinition> definition = new ArrayList<ParameterDefinition>(); + definition.add(new StringParameterDefinition("parameter","parameter-value")); + ParametersDefinitionProperty property = new ParametersDefinitionProperty(definition); + upstream.addProperty(property); + + AxisList axes = new AxisList(); + axes.add(new TextAxis("textAxis","a","b")); + upstream.setAxes(axes); + upstream.getBuildersList().add(new CreatePropertyFileBuilder()); + DefaultMatrixExecutionStrategyImpl strategy = new DefaultMatrixExecutionStrategyImpl(true, null, null, null); + upstream.setExecutionStrategy(strategy); + + List<AbstractBuildParameters> params = new ArrayList<AbstractBuildParameters>(); + params.add(new CurrentBuildParameters()); + List<AbstractBuildParameters> parameters = new ArrayList<AbstractBuildParameters>(); + parameters.add(new FileBuildParameters("property.prop", null, false, true, null, false)); + BuildTriggerConfig config = new BuildTriggerConfig("downstream", ResultCondition.SUCCESS, false, parameters, true); + BuildTrigger triggerBuilder = new BuildTrigger(config); + upstream.getPublishersList().add(triggerBuilder); + r.jenkins.rebuildDependencyGraph(); + strategy.setSorter(new MatrixConfigurationSorterTestImpl()); + + r.buildAndAssertSuccess(upstream); + r.waitUntilNoActivity(); + + FreeStyleBuild downstreamBuild2 = downstream.getLastBuild(); + FreeStyleBuild downstreamBuild1 = downstreamBuild2.getPreviousBuild(); + assertNotNull("Downstream job should be triggered by matrix configuration", downstreamBuild1); + assertNotNull("Downstream job should be triggered by matrix configuration", downstreamBuild2); + + String project1 = downstreamBuild1.getCause(Cause.UpstreamCause.class).getUpstreamProject(); + String project2 = downstreamBuild2.getCause(Cause.UpstreamCause.class).getUpstreamProject(); + ArrayList<MatrixConfiguration> configurations = Lists.newArrayList(upstream.getItems()); + Collections.sort(configurations, new MatrixConfigurationSorterTestImpl()); + assertEquals("Build should be triggered by matrix project.", configurations.get(0).getFullName(), project1); + assertEquals("Build should be triggered by matrix project.", configurations.get(1).getFullName(), project2); + } + + public static class MatrixConfigurationSorterTestImpl extends MatrixConfigurationSorter implements Serializable { + + @Override + public void validate(MatrixProject mp) throws FormValidation { + //do nothing + } + + @Override + public int compare(MatrixConfiguration o1, MatrixConfiguration o2) { + return o1.getName().compareTo(o2.getName()); + } + + } + + public static class CreatePropertyFileBuilder extends Builder { + + @Override + public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + listener.getLogger().println("Creating a file property.prop"); + + FilePath workspace = build.getWorkspace(); + if (workspace == null) { + throw new AbortException("Cannot get the workspace of the build"); + } + workspace.child("property.prop").write("value=" + build.getProject().getName(), "UTF-8"); + + return true; + } + } + +}