/* * Copyright 2013 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.jenkins.plugins.dsl; import java.io.IOException; import java.lang.reflect.Field; import java.util.List; import javax.annotation.Nonnull; import javax.servlet.ServletException; import org.acegisecurity.Authentication; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.jenkins.plugins.delegate.DelegateSCM; import com.google.jenkins.plugins.dsl.restrict.NoRestriction; import hudson.Extension; import hudson.model.AbstractProject; import hudson.model.Descriptor; import hudson.model.Descriptor.FormException; import hudson.model.ItemGroup; import hudson.model.Queue; import hudson.model.TopLevelItem; import hudson.model.View; import hudson.scm.SCMDescriptor; import hudson.security.ACL; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Publisher; import hudson.util.FormValidation; import jenkins.branch.BranchProjectFactory; import jenkins.branch.DefaultDeadBranchStrategy; import jenkins.branch.MultiBranchProject; import jenkins.branch.MultiBranchProjectDescriptor; import jenkins.model.Jenkins; import jenkins.scm.api.SCMSource; import jenkins.scm.api.SCMSourceCriteria; /** * A project kind designed scan branches for a marker DSL file, then * instantiate a {@link YamlProject} to parse and run it, on each matching * branch. * @param <T> The type of element contained * @see YamlProject */ public class YamlMultiBranchProject<T extends AbstractProject & TopLevelItem> extends MultiBranchProject<YamlProject<T>, YamlBuild<T>> { /** * Build up our Yaml multibranch project shell, which gets populated in * {@link #submit(StaplerRequest, StaplerResponse)}. */ public YamlMultiBranchProject(ItemGroup parent, String name) { super(parent, name); // By default, clean up branches that have been deleted. DefaultDeadBranchStrategy deadBranchStrategy = new DefaultDeadBranchStrategy( true /* prune dead branches */, "0" /* days to keep */, "0" /* num to keep */); // Monkey patch the better default. // TODO(mattmoor): upstream making the "deadBranchStrategy" // field protected instead of patching the object to set it here. { Class<?> clazz = YamlMultiBranchProject.class.getSuperclass(); Field field = null; for (Field iter : clazz.getDeclaredFields()) { if ("deadBranchStrategy".equals(iter.getName())) { field = iter; break; } } if (field == null) { throw new IllegalStateException( "deadBranchStrategy field missing from MultiBranchProject"); } try { field.setAccessible(true); field.set(this, deadBranchStrategy); } catch (IllegalAccessException e) { // Impossible, we have given ourselves access. } } deadBranchStrategy.setOwner(this); } /** {@inheritDoc} */ @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } /** {@inheritDoc} */ @Override public SCMSourceCriteria getSCMSourceCriteria(SCMSource source) { return new YamlCriteria(getProjectFactory().getYamlPath()); } /** The action of newView.jelly */ public synchronized void doCreateView(StaplerRequest req, StaplerResponse rsp) throws FormException, IOException, ServletException { addView(View.create(req, rsp, this)); } /** Makes sure the view name is available */ public FormValidation doCheckViewName(@QueryParameter String value) { if (Strings.isNullOrEmpty(value)) { return FormValidation.error( Messages.YamlMultiBranchProject_ViewNeedsName()); } if (getView(value) != null) { return FormValidation.error( Messages.YamlMultiBranchProject_ViewExists(value)); } return FormValidation.ok(); } /** {@inheritDoc} */ @Override @Nonnull public Authentication getDefaultAuthentication() { return ACL.SYSTEM; } /** {@inheritDoc} */ @Override @Nonnull public Authentication getDefaultAuthentication(Queue.Item item) { return getDefaultAuthentication(); } /** {@inheritDoc} */ @Override protected BranchProjectFactory<YamlProject<T>, YamlBuild<T>> newProjectFactory() { return new YamlProjectFactory(YamlProject.DEFAULT_YAML, new NoRestriction(), ImmutableList.<Publisher>of()); } /** {@inheritDoc} */ @Override public YamlProjectFactory getProjectFactory() { return (YamlProjectFactory) super.getProjectFactory(); } /** Boilerplate extension code */ @Extension public static class DescriptorImpl extends MultiBranchProjectDescriptor { /** {@inheritDoc} */ @Override public String getDisplayName() { return Messages.YamlMultiBranchProject_DisplayName(); } /** {@inheritDoc} */ @Override public boolean isApplicable(Descriptor descriptor) { // TODO(mattmoor): Ideally this would also ask the SCM via // something like: scmd.isApplicable(YamlMultiBranchProject.class) // So for now, just skip DelegateSCM: if (descriptor.clazz.equals(DelegateSCM.class)) { return false; } final YamlProject.DescriptorImpl yamlDescriptor = (YamlProject.DescriptorImpl) checkNotNull(Jenkins.getInstance()) .getDescriptor(YamlProject.class); return yamlDescriptor.isApplicable(descriptor); } /** Fetch the publisher options to present in our UI */ public List<Descriptor<Publisher>> getPublisherOptions() { // TODO(mattmoor): Avoid including non-@DataBoundConstructor publishers return BuildStepDescriptor.filter(Publisher.all(), YamlProject.class); } /** {@inheritDoc} */ @Override public List<SCMDescriptor<?>> getSCMDescriptors() { // I believe this is unused, and isApplicable is called instead? throw new UnsupportedOperationException(); } /** {@inheritDoc} */ @Override public YamlMultiBranchProject newInstance(ItemGroup parent, String name) { return new YamlMultiBranchProject(parent, name); } } }