Skip to content

Commit 6df2124

Browse files
authored
Merge pull request #160 from nfalco79/feature/JENKINS-48837
[JENKINS-48837] Add BranchProperty support to OrganizationFolder
2 parents 3662a43 + 4dc28f4 commit 6df2124

File tree

3 files changed

+134
-6
lines changed

3 files changed

+134
-6
lines changed

src/main/java/jenkins/branch/OrganizationFolder.java

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@
9898
import jenkins.scm.api.metadata.ObjectMetadataAction;
9999
import jenkins.scm.impl.SingleSCMNavigator;
100100
import jenkins.scm.impl.UncategorizedSCMSourceCategory;
101+
import net.sf.json.JSONObject;
102+
101103
import org.acegisecurity.AccessDeniedException;
102104
import org.acegisecurity.Authentication;
103105
import org.apache.commons.io.Charsets;
@@ -142,6 +144,13 @@ public final class OrganizationFolder extends ComputedFolder<MultiBranchProject<
142144
*/
143145
private DescribableList<BranchBuildStrategy, BranchBuildStrategyDescriptor> buildStrategies = new DescribableList<>(this);
144146

147+
/**
148+
* The branches properties.
149+
*
150+
* @since 2.5.9
151+
*/
152+
private BranchPropertyStrategy strategy;
153+
145154
/**
146155
* The persisted state maintained outside of the config file.
147156
*
@@ -163,6 +172,13 @@ public final class OrganizationFolder extends ComputedFolder<MultiBranchProject<
163172
*/
164173
private transient String facDigest;
165174

175+
/**
176+
* The {@link #propertyStrategy} digest used to detect if we need to trigger a rescan on save.
177+
*
178+
* @since 2.5.9
179+
*/
180+
private transient String propsDigest;
181+
166182
/**
167183
* The {@link #buildStrategies} digest used to detect if we need to trigger a rescan on save.
168184
*
@@ -266,6 +282,11 @@ public void onLoad(ItemGroup<? extends Item> parent, String name) throws IOExcep
266282
} catch (XStreamException e) {
267283
facDigest = null;
268284
}
285+
try {
286+
propsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(strategy));
287+
} catch (XStreamException e) {
288+
propsDigest = null;
289+
}
269290
try {
270291
bbsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(buildStrategies));
271292
} catch (XStreamException e) {
@@ -334,6 +355,26 @@ public DescribableList<MultiBranchProjectFactory,MultiBranchProjectFactoryDescri
334355
return projectFactories;
335356
}
336357

358+
/**
359+
* Gets the strategy.
360+
*
361+
* @return the strategy.
362+
* @since 2.5.9
363+
*/
364+
public BranchPropertyStrategy getStrategy() {
365+
return strategy != null ? strategy : new DefaultBranchPropertyStrategy(new BranchProperty[0]);
366+
}
367+
368+
/**
369+
* Sets the branch property strategy.
370+
*
371+
* @param strategy chosen.
372+
* @since 2.5.9
373+
*/
374+
public void setStrategy(BranchPropertyStrategy strategy) {
375+
this.strategy = strategy;
376+
}
377+
337378
/**
338379
* The {@link BranchBuildStrategy}s to apply.
339380
*
@@ -350,9 +391,13 @@ public DescribableList<BranchBuildStrategy, BranchBuildStrategyDescriptor> getBu
350391
@Override
351392
protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, Descriptor.FormException {
352393
super.submit(req, rsp);
353-
navigators.rebuildHetero(req, req.getSubmittedForm(), ExtensionList.lookup(SCMNavigatorDescriptor.class), "navigators");
354-
projectFactories.rebuildHetero(req, req.getSubmittedForm(), ExtensionList.lookup(MultiBranchProjectFactoryDescriptor.class), "projectFactories");
355-
buildStrategies.rebuildHetero(req, req.getSubmittedForm(), ExtensionList.lookup(BranchBuildStrategyDescriptor.class), "buildStrategies");
394+
395+
JSONObject json = req.getSubmittedForm();
396+
navigators.rebuildHetero(req, json, ExtensionList.lookup(SCMNavigatorDescriptor.class), "navigators");
397+
projectFactories.rebuildHetero(req, json, ExtensionList.lookup(MultiBranchProjectFactoryDescriptor.class), "projectFactories");
398+
buildStrategies.rebuildHetero(req, json, ExtensionList.lookup(BranchBuildStrategyDescriptor.class), "buildStrategies");
399+
strategy = req.bindJSON(BranchPropertyStrategy.class, json.getJSONObject("strategy"));
400+
356401
for (SCMNavigator n : navigators) {
357402
n.afterSave(this);
358403
}
@@ -368,6 +413,12 @@ protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOExceptio
368413
} catch (XStreamException e) {
369414
facDigest = null;
370415
}
416+
String propsDigest;
417+
try {
418+
propsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(strategy));
419+
} catch (XStreamException e) {
420+
propsDigest = null;
421+
}
371422
String bbsDigest;
372423
try {
373424
bbsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(buildStrategies));
@@ -376,9 +427,11 @@ protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOExceptio
376427
}
377428
recalculateAfterSubmitted(!StringUtils.equals(navDigest, this.navDigest));
378429
recalculateAfterSubmitted(!StringUtils.equals(facDigest, this.facDigest));
430+
recalculateAfterSubmitted(!StringUtils.equals(propsDigest, this.propsDigest));
379431
recalculateAfterSubmitted(!StringUtils.equals(bbsDigest, this.bbsDigest));
380432
this.navDigest = navDigest;
381433
this.facDigest = facDigest;
434+
this.propsDigest = propsDigest;
382435
this.bbsDigest = bbsDigest;
383436
}
384437

@@ -730,6 +783,16 @@ public String getCategoryId() {
730783
return "nested-projects";
731784
}
732785

786+
/**
787+
* Gets all the {@link BranchPropertyStrategyDescriptor} instances applicable to the specified project and source.
788+
*
789+
* @return all the {@link BranchPropertyStrategyDescriptor} instances applicable to the specified project and
790+
* source.
791+
*/
792+
public List<BranchPropertyStrategyDescriptor> propertyStrategyDescriptors() {
793+
return BranchPropertyStrategyDescriptor.all();
794+
}
795+
733796
/**
734797
* A description of this {@link OrganizationFolder}.
735798
*
@@ -1388,10 +1451,9 @@ private List<BranchSource> createBranchSources() {
13881451
for (SCMSource source : sources) {
13891452
BranchSource branchSource = new BranchSource(source);
13901453
branchSource.setBuildStrategies(buildStrategies);
1391-
// TODO do we want/need a more general BranchPropertyStrategyFactory?
1454+
branchSource.setStrategy(strategy);
13921455
branchSources.add(branchSource);
13931456
}
1394-
sources = null; // make sure complete gets called just once
13951457
return branchSources;
13961458
}
13971459

src/main/resources/jenkins/branch/OrganizationFolder/configure-entries.jelly

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
~ THE SOFTWARE.
2424
-->
2525
<?jelly escape-by-default='true'?>
26-
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler">
26+
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:st="jelly:stapler" xmlns:branch="/lib/branch-api">
2727
<f:section title="${%Projects}">
2828
<j:choose>
2929
<j:when test="${instance.singleOrigin}">
@@ -48,6 +48,12 @@
4848
<f:entry field="projectFactories" title="${%Project Recognizers}">
4949
<f:repeatableHeteroProperty field="projectFactories" hasHeader="true" />
5050
</f:entry>
51+
<j:set var="descriptors" value="${descriptor.propertyStrategyDescriptors()}"/>
52+
<j:if test="${!descriptors.isEmpty()}">
53+
<f:block>
54+
<branch:dropdownDescriptorSelector field="strategy" title="Property strategy" descriptors="${descriptors}"/>
55+
</f:block>
56+
</j:if>
5157
<j:set var="descriptors" value="${descriptor.getPropertyType(instance,'buildStrategies').applicableItemDescriptors}"/>
5258
<j:if test="${!descriptors.isEmpty()}">
5359
<f:entry title="${%Build strategies}" help="${descriptor.getHelpFile('buildStrategies')}">

src/test/java/jenkins/branch/OrganizationFolderTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
import hudson.ExtensionList;
3030
import hudson.model.Item;
3131
import hudson.model.ItemGroup;
32+
import hudson.model.ParameterDefinition;
3233
import hudson.model.Result;
34+
import hudson.model.StringParameterDefinition;
3335
import hudson.model.TaskListener;
3436
import hudson.model.View;
3537
import hudson.scm.NullSCM;
3638
import hudson.security.Permission;
39+
import integration.harness.BasicMultiBranchProject;
3740
import integration.harness.BasicMultiBranchProjectFactory;
3841
import java.io.IOException;
3942
import java.util.Arrays;
@@ -75,6 +78,22 @@
7578

7679
public class OrganizationFolderTest {
7780

81+
public static class OrganizationFolderBranchProperty extends ParameterDefinitionBranchProperty {
82+
83+
@DataBoundConstructor
84+
public OrganizationFolderBranchProperty() {
85+
super();
86+
}
87+
88+
@TestExtension
89+
public static class DescriptorImpl extends BranchPropertyDescriptor {
90+
@Override
91+
protected boolean isApplicable(MultiBranchProjectDescriptor projectDescriptor) {
92+
return projectDescriptor instanceof BasicMultiBranchProject.DescriptorImpl;
93+
}
94+
}
95+
}
96+
7897
@Rule
7998
public JenkinsRule r = new JenkinsRule();
8099

@@ -103,6 +122,47 @@ public void configRoundTrip() throws Exception {
103122
}
104123
}
105124

125+
@Issue("JENKINS-48837")
126+
@Test
127+
public void verifyBranchPropertiesAppliedOnNewProjects() throws Exception {
128+
try (MockSCMController c = MockSCMController.create()) {
129+
c.createRepository("stuff");
130+
OrganizationFolder top = r.jenkins.createProject(OrganizationFolder.class, "top");
131+
List<MultiBranchProjectFactory> projectFactories = top.getProjectFactories();
132+
assertEquals(1, projectFactories.size());
133+
assertEquals(MockFactory.class, projectFactories.get(0).getClass());
134+
top.getNavigators().add(new SingleSCMNavigator("stuff",
135+
Collections.<SCMSource>singletonList(new SingleSCMSource("stuffy",
136+
new MockSCM(c, "stuff", new MockSCMHead("master"), null))))
137+
);
138+
OrganizationFolderBranchProperty instance = new OrganizationFolderBranchProperty();
139+
instance.setParameterDefinitions(Collections.<ParameterDefinition>singletonList(
140+
new StringParameterDefinition("PARAM_STR", "PARAM_DEFAULT_0812673", "The param")
141+
));
142+
top.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[] { instance }));
143+
top = r.configRoundtrip(top);
144+
145+
top.scheduleBuild(0);
146+
r.waitUntilNoActivity();
147+
148+
// get the child project produced by the factory after scan
149+
MultiBranchImpl prj = (MultiBranchImpl) top.getItem("stuff");
150+
// verify new multibranch project have branch properties inherited from folder
151+
assertThat(prj.getSources().get(0).getStrategy(), instanceOf(DefaultBranchPropertyStrategy.class));
152+
DefaultBranchPropertyStrategy strategy = (DefaultBranchPropertyStrategy) prj.getSources().get(0).getStrategy();
153+
assertThat(strategy.getProps().get(0), instanceOf(OrganizationFolderBranchProperty.class));
154+
OrganizationFolderBranchProperty property = (OrganizationFolderBranchProperty) strategy.getProps().get(0);
155+
assertThat(property.getParameterDefinitions(), contains(
156+
allOf(
157+
instanceOf(StringParameterDefinition.class),
158+
hasProperty("name", is("PARAM_STR")),
159+
hasProperty("defaultValue", is("PARAM_DEFAULT_0812673")),
160+
hasProperty("description", is("The param"))
161+
)
162+
));
163+
}
164+
}
165+
106166
@Test
107167
@Issue("JENKINS-31516")
108168
public void indexChildrenOnOrganizationFolderIndex() throws Exception {

0 commit comments

Comments
 (0)