Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-43507] Mercurial would have to be difficult and need a regex
  • Loading branch information
stephenc committed May 4, 2017
1 parent 0b1cc9f commit a01f955
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 1 deletion.
147 changes: 147 additions & 0 deletions src/main/java/jenkins/scm/impl/trait/RegexSCMHeadFilterTrait.java
@@ -0,0 +1,147 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* 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 jenkins.scm.impl.trait;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.util.FormValidation;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMNavigator;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.trait.SCMHeadPrefilter;
import jenkins.scm.api.trait.SCMNavigatorContext;
import jenkins.scm.api.trait.SCMNavigatorRequest;
import jenkins.scm.api.trait.SCMNavigatorTrait;
import jenkins.scm.api.trait.SCMNavigatorTraitDescriptor;
import jenkins.scm.api.trait.SCMSourceContext;
import jenkins.scm.api.trait.SCMSourcePrefilter;
import jenkins.scm.api.trait.SCMSourceRequest;
import jenkins.scm.api.trait.SCMSourceTrait;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;

/**
* Decorates a {@link SCMSource} with a {@link SCMHeadPrefilter} that excludes {@link SCMHead} instances with names that
* do not match a user supplied regular expression.
*
* @since 2.2.0
*/
public class RegexSCMHeadFilterTrait extends SCMSourceTrait {

/**
* The regular expression.
*/
@NonNull
private final String regex;
/**
* The compiled {@link Pattern}.
*/
@CheckForNull
private transient Pattern pattern;

/**
* Stapler constructor.
*
* @param regex the regular expression.
*/
@DataBoundConstructor
public RegexSCMHeadFilterTrait(@NonNull String regex) {
pattern = Pattern.compile(regex);
this.regex = regex;
}

/**
* Gets the regular expression.
*
* @return the regular expression.
*/
@NonNull
public String getRegex() {
return regex;
}

/**
* Gets the compiled {@link Pattern}.
*
* @return the compiled {@link Pattern}.
*/
@NonNull
private Pattern getPattern() {
if (pattern == null) {
// idempotent
pattern = Pattern.compile(regex);
}
return pattern;
}

/**
* {@inheritDoc}
*/
@Override
protected <B extends SCMSourceContext<B, R>, R extends SCMSourceRequest> void decorateContext(B context) {
context.withPrefilter(new SCMHeadPrefilter() {
@Override
public boolean isExcluded(@NonNull SCMSource source, @NonNull SCMHead head) {
return !getPattern().matcher(head.getName()).matches();
}
});
}

/**
* Our descriptor.
*/
@Extension
public static class DescriptorImpl extends SCMNavigatorTraitDescriptor {

/**
* {@inheritDoc}
*/
@Override
public String getDisplayName() {
return Messages.RegexSCMHeadFilterTrait_DisplayName();
}

/**
* Form validation for the regular expression.
*
* @param value the regular expression.
* @return the validation results.
*/
@Restricted(NoExternalUse.class) // stapler
public FormValidation doCheckRegex(@QueryParameter String value) {
try {
Pattern.compile(value);
return FormValidation.ok();
} catch (PatternSyntaxException e) {
return FormValidation.error(e.getMessage());
}
}
}
}
Expand Up @@ -39,7 +39,7 @@
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Decorates a {@link SCMSourceTrait} with a {@link SCMHeadPrefilter} that filters {@link SCMHead} instances based on
* Decorates a {@link SCMSource} with a {@link SCMHeadPrefilter} that filters {@link SCMHead} instances based on
* matching wildcard include/exclude rules.
*
* @since 2.2.0
Expand Down
Expand Up @@ -23,4 +23,5 @@
#
WildcardSCMHeadFilterTrait.DisplayName=Filter by name (with wildcards)
WildcardSCMSourceFilterTrait.DisplayName=Filter projects by name (with wildcards)
RegexSCMHeadFilterTrait.DisplayName=Filter by name (with regular expression)
RegexSCMSourceFilterTrait.DisplayName=Filter projects by name (with regular expression)
@@ -0,0 +1,29 @@
<!--
~ The MIT License
~
~ Copyright (c) 2017, CloudBees, Inc.
~
~ 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.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry title="${%Regular expression}" field="regex">
<f:textbox default=".*"/>
</f:entry>
</j:jelly>
@@ -0,0 +1,27 @@
<!--
~ The MIT License
~
~ Copyright (c) 2017, CloudBees, Inc.
~
~ 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.
-->
<div>
A <a href="https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">Java regular expression</a> to
restrict the names. Names that do not match the supplied regular expression will be ignored.
</div>
@@ -0,0 +1,65 @@
/*
* The MIT License
*
* Copyright (c) 2017, CloudBees, Inc.
*
* 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 jenkins.scm.impl.trait;

import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.impl.mock.MockSCMController;
import jenkins.scm.impl.mock.MockSCMDiscoverBranches;
import jenkins.scm.impl.mock.MockSCMSource;
import org.junit.ClassRule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;

public class RegexSCMHeadFilterTraitTest {
@ClassRule
public static JenkinsRule r = new JenkinsRule();

@Test
public void given_sourceWithRegexRule_when_scanning_then_ruleApplied() throws Exception {
MockSCMController c = MockSCMController.create();
try {
c.createRepository("foo");
c.createBranch("foo", "fork");
c.createBranch("foo", "alt");
MockSCMSource src = new MockSCMSource("foo", c, "foo", new MockSCMDiscoverBranches(), new RegexSCMHeadFilterTrait("[fm].*"));
Map<SCMHead, SCMRevision> result = src.fetch(null, SCMHeadObserver.collect(), null, null).result();
Set<String> names = new TreeSet<String>();
for (SCMHead h: result.keySet()) {
names.add(h.getName());
}
assertThat(names, containsInAnyOrder("master", "fork"));
} finally {
c.close();
}
}
}

0 comments on commit a01f955

Please sign in to comment.