diff --git a/pom.xml b/pom.xml index d85aadc5..4648401f 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ org.jenkins-ci.plugins cloudbees-folder - 5.1 + 6.0.1 true @@ -117,6 +117,12 @@ 4.3.4 jar + + org.jenkins-ci.plugins + branch-api + 2.0.8 + jar + org.jenkins-ci.plugins.workflow workflow-cps diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy.java b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy.java new file mode 100644 index 00000000..1f6b5335 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy.java @@ -0,0 +1,116 @@ +/* + * The MIT License + * + * Copyright (c) 2017 Jordan Coll + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.ownership.model.branches; + +import com.synopsys.arc.jenkins.plugins.ownership.Messages; +import hudson.Extension; +import hudson.Util; +import hudson.model.User; +import hudson.util.FormValidation; +import jenkins.branch.Branch; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.annotation.RegEx; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +/** + * A {@link BranchOwnershipStrategy} that determines ownership by matching branch names against a {@link Pattern regular expression}. + */ +public class BranchNameOwnershipStrategy extends BranchOwnershipStrategy { + + private Pattern pattern; + private String ownerExpression; + + /** + * Constructs an instance. + * + * @param pattern A {@link Pattern regular expression} against which branch names should be matched + * @param ownerExpression A string representing the prospective owner. May contain references to capture groups in the pattern. + * See {@link Matcher#appendReplacement(StringBuffer, String)} for syntax. + * @throws PatternSyntaxException If the pattern's syntax is invalid + */ + @DataBoundConstructor + public BranchNameOwnershipStrategy(@RegEx String pattern, @Nonnull String ownerExpression) throws PatternSyntaxException { + this.pattern = Pattern.compile(pattern); + this.ownerExpression = ownerExpression; + } + + public String getPattern() { + return pattern.pattern(); + } + + public String getOwnerExpression() { + return ownerExpression; + } + + @Override + @Nullable + public String determineOwner(Branch branch) { + Matcher matcher = pattern.matcher(branch.getName()); + + if (matcher.matches()) { + String prospectiveOwner = matcher.replaceAll(ownerExpression); + + if (User.get(prospectiveOwner, false, Collections.emptyMap()) != null) { + return prospectiveOwner; + } + } + + return null; + } + + @Extension + public static class DescriptorImpl extends BranchOwnershipStrategy.BranchOwnershipStrategyDescriptor { + + @Override + @Nonnull + public String getDisplayName() { + return Messages.BranchOwnership_Strategy_BranchNameOwnershipStrategy_DisplayName(); + } + + @Restricted(NoExternalUse.class) + public FormValidation doCheckPattern(@QueryParameter String value) { + try { + Pattern.compile(value); + } catch (PatternSyntaxException ex) { + return FormValidation.error(Messages.BranchOwnership_Strategy_BranchNameOwnershipStrategy_InvalidRegex(ex.getMessage())); + } + + return FormValidation.ok(); + } + + @Restricted(NoExternalUse.class) + public FormValidation doCheckOwnerExpression(@QueryParameter String value) { + return Util.fixEmptyAndTrim(value) != null ? FormValidation.ok() : FormValidation.warning("Ownership will be disabled"); + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/branches/BranchOwnershipStrategy.java b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/BranchOwnershipStrategy.java new file mode 100644 index 00000000..f884f52e --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/BranchOwnershipStrategy.java @@ -0,0 +1,54 @@ +/* + * The MIT License + * + * Copyright (c) 2017 Jordan Coll + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.ownership.model.branches; + +import hudson.ExtensionPoint; +import hudson.model.AbstractDescribableImpl; +import hudson.model.Descriptor; +import jenkins.branch.Branch; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; + +public abstract class BranchOwnershipStrategy extends AbstractDescribableImpl implements ExtensionPoint { + + /** + * Determine the owner for the given branch using the implemented strategy. + * + * @param branch The branch + * @return The prospective owner's user ID or full name. {@code null} if the owner cannot be determined. + */ + @CheckForNull + public abstract String determineOwner(Branch branch); + + @Nonnull + @SuppressWarnings("unchecked") + public BranchOwnershipStrategyDescriptor getDescriptor() { + return (BranchOwnershipStrategyDescriptor) super.getDescriptor(); + } + + static abstract class BranchOwnershipStrategyDescriptor extends Descriptor { + + } +} diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/branches/FromScmBranchOwnershipStrategy.java b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/FromScmBranchOwnershipStrategy.java new file mode 100644 index 00000000..189722f0 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/FromScmBranchOwnershipStrategy.java @@ -0,0 +1,53 @@ +/* + * The MIT License + * + * Copyright (c) 2017 Jordan Coll + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.ownership.model.branches; + + +import com.synopsys.arc.jenkins.plugins.ownership.Messages; +import hudson.Extension; +import jenkins.branch.Branch; +import jenkins.scm.api.metadata.ContributorMetadataAction; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class FromScmBranchOwnershipStrategy extends BranchOwnershipStrategy { + + + @Nullable + @Override + public String determineOwner(Branch branch) { + ContributorMetadataAction contributorMetadataAction = branch.getAction(ContributorMetadataAction.class); + return contributorMetadataAction != null ? contributorMetadataAction.getContributor() : null; + } + + @Extension + public static class DescriptorImpl extends BranchOwnershipStrategy.BranchOwnershipStrategyDescriptor { + @Override + @Nonnull + public String getDisplayName() { + return Messages.BranchOwnership_Strategy_FromScmOwnershipStrategy_DisplayName(); + } + } +} diff --git a/src/main/java/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty.java b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty.java new file mode 100644 index 00000000..ab38a1d2 --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty.java @@ -0,0 +1,112 @@ +/* + * The MIT License + * + * Copyright (c) 2017 Jordan Coll + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jenkinsci.plugins.ownership.model.branches; + +import com.synopsys.arc.jenkins.plugins.ownership.Messages; +import com.synopsys.arc.jenkins.plugins.ownership.OwnershipDescription; +import com.synopsys.arc.jenkins.plugins.ownership.OwnershipPlugin; +import com.synopsys.arc.jenkins.plugins.ownership.jobs.JobOwnerHelper; +import hudson.Extension; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.TopLevelItem; +import hudson.model.User; +import hudson.util.FormValidation; +import jenkins.branch.Branch; +import jenkins.branch.BranchProperty; +import jenkins.branch.BranchPropertyDescriptor; +import jenkins.branch.JobDecorator; +import jenkins.branch.MultiBranchProject; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.Collections; +import java.util.logging.Level; + +public class OwnershipBranchProperty extends BranchProperty { + + private BranchOwnershipStrategy strategy; + private String fallbackOwner; + + @DataBoundConstructor + public OwnershipBranchProperty(@Nonnull String fallbackOwner, @Nonnull BranchOwnershipStrategy strategy) { + this.strategy = strategy; + this.fallbackOwner = fallbackOwner; + } + + public BranchOwnershipStrategy getStrategy() { + return strategy; + } + + public String getFallbackOwner() { + return fallbackOwner; + } + + @Override + @SuppressWarnings("unchecked") + public

, B extends Run> JobDecorator jobDecorator(final Class

clazz) { + return new JobDecorator() { + @Nonnull + public P project(@Nonnull P project) { + if (project.getParent() instanceof MultiBranchProject && TopLevelItem.class.isAssignableFrom(clazz)) { + MultiBranchProject multiBranchProject = (MultiBranchProject) project.getParent(); + Branch branch = multiBranchProject.getProjectFactory().getBranch(project); + + String prospectiveOwner = strategy.determineOwner(branch); + String owner = prospectiveOwner != null ? prospectiveOwner : getFallbackOwner(); + + OwnershipDescription ownershipDescription = new OwnershipDescription(true, owner, null); + try { + JobOwnerHelper.setOwnership(project, ownershipDescription); + } catch (IOException ioe) { + // TODO: handle somehow + String msg = String.format("Failed setting owner for branch %s in project %s", + branch.getName(), multiBranchProject.getFullName()); + OwnershipPlugin.getLogger().log(Level.SEVERE, msg, ioe); + } + } + return project; + } + }; + } + + @Extension + public static class DescriptorImpl extends BranchPropertyDescriptor { + @Nonnull + @Override + public String getDisplayName() { + return Messages.BranchOwnership_BranchProperty_DisplayName(); + } + + @Restricted(NoExternalUse.class) + public FormValidation doCheckFallbackOwner(@QueryParameter String value) { + User user = User.get(value, false, Collections.emptyMap()); + return user != null ? FormValidation.ok() : FormValidation.error(Messages.BranchOwnership_BranchProperty_UnknownUserError(), value); + } + } +} diff --git a/src/main/resources/com/synopsys/arc/jenkins/plugins/ownership/Messages.properties b/src/main/resources/com/synopsys/arc/jenkins/plugins/ownership/Messages.properties index b35c9ea0..fe910fa2 100644 --- a/src/main/resources/com/synopsys/arc/jenkins/plugins/ownership/Messages.properties +++ b/src/main/resources/com/synopsys/arc/jenkins/plugins/ownership/Messages.properties @@ -35,6 +35,12 @@ OwnershipAction.ConfigureSpecificAccess.DisplayName=Configure specific access ri Utils.UI.UserSelector=User ID +BranchOwnership.BranchProperty.DisplayName=Set branch job ownership +BranchOwnership.BranchProperty.UnknownUserError=User is not registered in Jenkins +BranchOwnership.Strategy.BranchNameOwnershipStrategy.DisplayName=By branch name +BranchOwnership.Strategy.BranchNameOwnershipStrategy.InvalidRegex=Invalid regex: {0} +BranchOwnership.Strategy.FromScmOwnershipStrategy.DisplayName=From SCM + # Extensions ItemOwnershipPolicy.AssignCreatorPolicy.dipslayName=Assign job creators as owners ItemOwnershipPolicy.DropOwnershipPolicy.dipslayName=Do not assign ownership diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/config.jelly b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/config.jelly new file mode 100644 index 00000000..74378513 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/config.jelly @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/help-ownerExpression.html b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/help-ownerExpression.html new file mode 100644 index 00000000..2e763bef --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/help-ownerExpression.html @@ -0,0 +1,6 @@ +

+ The expression used to determine the owner. + May contain references to capture groups in the capture pattern using $index or $name. + See + java.util.regex.Matcher for details. +
diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/help-pattern.html b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/help-pattern.html new file mode 100644 index 00000000..86c7203e --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/BranchNameOwnershipStrategy/help-pattern.html @@ -0,0 +1,4 @@ +
+ A regular expression matching the branch name. Capture groups can be included and used in the owner expression. + See java.util.regex.Pattern for syntax. +
diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/FromScmBranchOwnershipStrategy/help.html b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/FromScmBranchOwnershipStrategy/help.html new file mode 100644 index 00000000..30163a5b --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/FromScmBranchOwnershipStrategy/help.html @@ -0,0 +1,6 @@ +
+ SCM sources can specify the "contributor" of a branch. + For example, GitHub and Bitbucket Branch Source plugins specify the user who opened a PR as its contributor. + + This strategy sets this contributor as the owner of the branch job. +
diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty/config.jelly b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty/config.jelly new file mode 100644 index 00000000..37edd4a9 --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty/config.jelly @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty/help-fallbackOwner.html b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty/help-fallbackOwner.html new file mode 100644 index 00000000..8ce897ee --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/ownership/model/branches/OwnershipBranchProperty/help-fallbackOwner.html @@ -0,0 +1,3 @@ +
+ If the chosen strategy cannot determine the owner, the fallback owner will be used. +