diff --git a/src/main/java/jenkins/branch/OrganizationFolder.java b/src/main/java/jenkins/branch/OrganizationFolder.java index 6a94d206..4e8e0bb3 100644 --- a/src/main/java/jenkins/branch/OrganizationFolder.java +++ b/src/main/java/jenkins/branch/OrganizationFolder.java @@ -98,6 +98,8 @@ import jenkins.scm.api.metadata.ObjectMetadataAction; import jenkins.scm.impl.SingleSCMNavigator; import jenkins.scm.impl.UncategorizedSCMSourceCategory; +import net.sf.json.JSONObject; + import org.acegisecurity.AccessDeniedException; import org.acegisecurity.Authentication; import org.apache.commons.io.Charsets; @@ -142,6 +144,13 @@ public final class OrganizationFolder extends ComputedFolder buildStrategies = new DescribableList<>(this); + /** + * The branches properties. + * + * @since 2.5.9 + */ + private BranchPropertyStrategy strategy; + /** * The persisted state maintained outside of the config file. * @@ -163,6 +172,13 @@ public final class OrganizationFolder extends ComputedFolder parent, String name) throws IOExcep } catch (XStreamException e) { facDigest = null; } + try { + propsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(strategy)); + } catch (XStreamException e) { + propsDigest = null; + } try { bbsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(buildStrategies)); } catch (XStreamException e) { @@ -334,6 +355,26 @@ public DescribableList getBu @Override protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException, Descriptor.FormException { super.submit(req, rsp); - navigators.rebuildHetero(req, req.getSubmittedForm(), ExtensionList.lookup(SCMNavigatorDescriptor.class), "navigators"); - projectFactories.rebuildHetero(req, req.getSubmittedForm(), ExtensionList.lookup(MultiBranchProjectFactoryDescriptor.class), "projectFactories"); - buildStrategies.rebuildHetero(req, req.getSubmittedForm(), ExtensionList.lookup(BranchBuildStrategyDescriptor.class), "buildStrategies"); + + JSONObject json = req.getSubmittedForm(); + navigators.rebuildHetero(req, json, ExtensionList.lookup(SCMNavigatorDescriptor.class), "navigators"); + projectFactories.rebuildHetero(req, json, ExtensionList.lookup(MultiBranchProjectFactoryDescriptor.class), "projectFactories"); + buildStrategies.rebuildHetero(req, json, ExtensionList.lookup(BranchBuildStrategyDescriptor.class), "buildStrategies"); + strategy = req.bindJSON(BranchPropertyStrategy.class, json.getJSONObject("strategy")); + for (SCMNavigator n : navigators) { n.afterSave(this); } @@ -368,6 +413,12 @@ protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOExceptio } catch (XStreamException e) { facDigest = null; } + String propsDigest; + try { + propsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(strategy)); + } catch (XStreamException e) { + propsDigest = null; + } String bbsDigest; try { bbsDigest = Util.getDigestOf(Items.XSTREAM2.toXML(buildStrategies)); @@ -376,9 +427,11 @@ protected void submit(StaplerRequest req, StaplerResponse rsp) throws IOExceptio } recalculateAfterSubmitted(!StringUtils.equals(navDigest, this.navDigest)); recalculateAfterSubmitted(!StringUtils.equals(facDigest, this.facDigest)); + recalculateAfterSubmitted(!StringUtils.equals(propsDigest, this.propsDigest)); recalculateAfterSubmitted(!StringUtils.equals(bbsDigest, this.bbsDigest)); this.navDigest = navDigest; this.facDigest = facDigest; + this.propsDigest = propsDigest; this.bbsDigest = bbsDigest; } @@ -730,6 +783,16 @@ public String getCategoryId() { return "nested-projects"; } + /** + * Gets all the {@link BranchPropertyStrategyDescriptor} instances applicable to the specified project and source. + * + * @return all the {@link BranchPropertyStrategyDescriptor} instances applicable to the specified project and + * source. + */ + public List propertyStrategyDescriptors() { + return BranchPropertyStrategyDescriptor.all(); + } + /** * A description of this {@link OrganizationFolder}. * @@ -1388,10 +1451,9 @@ private List createBranchSources() { for (SCMSource source : sources) { BranchSource branchSource = new BranchSource(source); branchSource.setBuildStrategies(buildStrategies); - // TODO do we want/need a more general BranchPropertyStrategyFactory? + branchSource.setStrategy(strategy); branchSources.add(branchSource); } - sources = null; // make sure complete gets called just once return branchSources; } diff --git a/src/main/resources/jenkins/branch/OrganizationFolder/configure-entries.jelly b/src/main/resources/jenkins/branch/OrganizationFolder/configure-entries.jelly index 1bbf53c9..c4aada43 100644 --- a/src/main/resources/jenkins/branch/OrganizationFolder/configure-entries.jelly +++ b/src/main/resources/jenkins/branch/OrganizationFolder/configure-entries.jelly @@ -23,7 +23,7 @@ ~ THE SOFTWARE. --> - + @@ -48,6 +48,12 @@ + + + + + + diff --git a/src/test/java/jenkins/branch/OrganizationFolderTest.java b/src/test/java/jenkins/branch/OrganizationFolderTest.java index d619a045..76a9c53d 100644 --- a/src/test/java/jenkins/branch/OrganizationFolderTest.java +++ b/src/test/java/jenkins/branch/OrganizationFolderTest.java @@ -29,11 +29,14 @@ import hudson.ExtensionList; import hudson.model.Item; import hudson.model.ItemGroup; +import hudson.model.ParameterDefinition; import hudson.model.Result; +import hudson.model.StringParameterDefinition; import hudson.model.TaskListener; import hudson.model.View; import hudson.scm.NullSCM; import hudson.security.Permission; +import integration.harness.BasicMultiBranchProject; import integration.harness.BasicMultiBranchProjectFactory; import java.io.IOException; import java.util.Arrays; @@ -75,6 +78,22 @@ public class OrganizationFolderTest { + public static class OrganizationFolderBranchProperty extends ParameterDefinitionBranchProperty { + + @DataBoundConstructor + public OrganizationFolderBranchProperty() { + super(); + } + + @TestExtension + public static class DescriptorImpl extends BranchPropertyDescriptor { + @Override + protected boolean isApplicable(MultiBranchProjectDescriptor projectDescriptor) { + return projectDescriptor instanceof BasicMultiBranchProject.DescriptorImpl; + } + } + } + @Rule public JenkinsRule r = new JenkinsRule(); @@ -103,6 +122,47 @@ public void configRoundTrip() throws Exception { } } + @Issue("JENKINS-48837") + @Test + public void verifyBranchPropertiesAppliedOnNewProjects() throws Exception { + try (MockSCMController c = MockSCMController.create()) { + c.createRepository("stuff"); + OrganizationFolder top = r.jenkins.createProject(OrganizationFolder.class, "top"); + List projectFactories = top.getProjectFactories(); + assertEquals(1, projectFactories.size()); + assertEquals(MockFactory.class, projectFactories.get(0).getClass()); + top.getNavigators().add(new SingleSCMNavigator("stuff", + Collections.singletonList(new SingleSCMSource("stuffy", + new MockSCM(c, "stuff", new MockSCMHead("master"), null)))) + ); + OrganizationFolderBranchProperty instance = new OrganizationFolderBranchProperty(); + instance.setParameterDefinitions(Collections.singletonList( + new StringParameterDefinition("PARAM_STR", "PARAM_DEFAULT_0812673", "The param") + )); + top.setStrategy(new DefaultBranchPropertyStrategy(new BranchProperty[] { instance })); + top = r.configRoundtrip(top); + + top.scheduleBuild(0); + r.waitUntilNoActivity(); + + // get the child project produced by the factory after scan + MultiBranchImpl prj = (MultiBranchImpl) top.getItem("stuff"); + // verify new multibranch project have branch properties inherited from folder + assertThat(prj.getSources().get(0).getStrategy(), instanceOf(DefaultBranchPropertyStrategy.class)); + DefaultBranchPropertyStrategy strategy = (DefaultBranchPropertyStrategy) prj.getSources().get(0).getStrategy(); + assertThat(strategy.getProps().get(0), instanceOf(OrganizationFolderBranchProperty.class)); + OrganizationFolderBranchProperty property = (OrganizationFolderBranchProperty) strategy.getProps().get(0); + assertThat(property.getParameterDefinitions(), contains( + allOf( + instanceOf(StringParameterDefinition.class), + hasProperty("name", is("PARAM_STR")), + hasProperty("defaultValue", is("PARAM_DEFAULT_0812673")), + hasProperty("description", is("The param")) + ) + )); + } + } + @Test @Issue("JENKINS-31516") public void indexChildrenOnOrganizationFolderIndex() throws Exception {