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

Add possibility to set downstream projects for child projects. #106

Merged
merged 4 commits into from
Feb 21, 2017
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import hudson.model.BuildListener;
import hudson.model.DependecyDeclarer;
import hudson.model.DependencyGraph;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.model.Job;
Expand All @@ -19,6 +21,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

Expand Down Expand Up @@ -96,7 +99,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.triggerFromChildProjects() && owner instanceof ItemGroup) {
ItemGroup<Item> parent = (ItemGroup) owner;
for (Item item : parent.getItems()) {
if(item instanceof AbstractProject){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would vote against using AbstractProject in the new code, but it requires rework of ParameterizedDependency

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for all response, I put it into my code. I am afraid that only reworking ParametrizedDependency does not help.... https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/model/DependencyGraph.java#L394

AbstractProject child = (AbstractProject) item;
ParameterizedDependency.add(child, project, config, graph);
}
}
}
else{
ParameterizedDependency.add(owner, project, config, graph);

}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,29 @@ 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;
}

public BuildTriggerConfig(String projects, ResultCondition condition,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this method needs to be deprecated

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);
Expand Down Expand Up @@ -116,6 +128,10 @@ public ResultCondition getCondition() {
public boolean getTriggerWithNoParameters() {
return triggerWithNoParameters;
}

public boolean triggerFromChildProjects(){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace by isTriggerFromChildProjects to follow the common naming convention

return triggerFromChildProjects;
}

/**
* @deprecated
Expand Down Expand Up @@ -630,6 +646,10 @@ public List<Descriptor<AbstractBuildParameterFactory>> getBuilderConfigFactoryDe
return Hudson.getInstance().<AbstractBuildParameterFactory,
Descriptor<AbstractBuildParameterFactory>>getDescriptorList(AbstractBuildParameterFactory.class);
}

public boolean isItemGroup(AbstractProject project){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to Restrict this method by @Restricted(DoNotUse.class) since it is only for UI

return project instanceof ItemGroup;
}

/**
* Form validation method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.triggerFromChildProjects()}"/>
</f:entry>
</j:if>
<f:block>
<f:hetero-list name="configs" hasHeader="true"
descriptors="${descriptor.getBuilderConfigDescriptors()}"
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}

}