Skip to content

Commit

Permalink
[JENKINS-46064] Validator for changelog regular expression
Browse files Browse the repository at this point in the history
  • Loading branch information
rsandell committed Aug 18, 2017
1 parent ebc4787 commit c21c3d6
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 1 deletion.
Expand Up @@ -26,12 +26,15 @@
package org.jenkinsci.plugins.pipeline.modeldefinition.when.impl;

import hudson.Extension;
import hudson.RestrictedSince;
import org.codehaus.groovy.ast.expr.Expression;
import org.jenkinsci.Symbol;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhenContent;
import org.jenkinsci.plugins.pipeline.modeldefinition.parser.ASTParserUtils;
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditional;
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditionalDescriptor;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;

import javax.annotation.CheckForNull;
Expand All @@ -51,7 +54,7 @@ public class ChangeLogConditional extends DeclarativeStageConditional<ChangeLogC
public ChangeLogConditional(String pattern) {
//TODO JENKINS-46065 validate the regexp when #179 is merged
this.pattern = Pattern.compile(pattern);
this.multiLinePattern = Pattern.compile("(?m)(?s)^[^\\r\\n]*?" + pattern + "[^\\r\\n]*?$",
this.multiLinePattern = Pattern.compile(expandForMultiLine(pattern),
Pattern.MULTILINE | Pattern.DOTALL);
}

Expand All @@ -71,4 +74,9 @@ public Expression transformToRuntimeAST(@CheckForNull ModelASTWhenContent origin
return ASTParserUtils.transformWhenContentToRuntimeAST(original);
}
}

@Restricted(NoExternalUse.class)
public static String expandForMultiLine(String pattern) {
return "(?m)(?s)^[^\\r\\n]*?" + pattern + "[^\\r\\n]*?$";
}
}
@@ -0,0 +1,82 @@
/*
* 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 org.jenkinsci.plugins.pipeline.modeldefinition.when.impl;

import hudson.Extension;
import org.jenkinsci.plugins.pipeline.modeldefinition.Messages;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTArgumentList;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTNamedArgumentList;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTPositionalArgumentList;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTSingleArgument;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTValue;
import org.jenkinsci.plugins.pipeline.modeldefinition.ast.ModelASTWhenCondition;
import org.jenkinsci.plugins.pipeline.modeldefinition.validator.DeclarativeValidatorContributor;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

@Extension
public class WhenConditionalValidator extends DeclarativeValidatorContributor {

@CheckForNull
@Override
public String validateElement(@Nonnull ModelASTWhenCondition condition, @CheckForNull FlowExecution execution) {
if (condition.getName().equals("changelog")) {
String pattern = getPatternArgument(condition.getArgs());
if (pattern == null) {
return Messages.WhenConditionalValidator_changelog_missingParameter();
} else {
try {
Pattern.compile(pattern);
Pattern.compile(ChangeLogConditional.expandForMultiLine(pattern), Pattern.MULTILINE | Pattern.DOTALL);
} catch (PatternSyntaxException e) {
return Messages.WhenConditionalValidator_changelog_badPattern(pattern, e.getMessage());
}
}
}

return null;
}

private String getPatternArgument(ModelASTArgumentList args) {
if (args instanceof ModelASTSingleArgument) {
return (String) ((ModelASTSingleArgument) args).getValue().getValue();
} else if (args instanceof ModelASTPositionalArgumentList) {
final List<ModelASTValue> arguments = ((ModelASTPositionalArgumentList) args).getArguments();
if (!arguments.isEmpty()) {
return (String) arguments.get(0).getValue();
}
} else if (args instanceof ModelASTNamedArgumentList) {
return (String) ((ModelASTNamedArgumentList) args).argListToMap().get("pattern");
}

return null;
}
}
Expand Up @@ -142,4 +142,7 @@ ModelValidatorImpl.AgentInNestedStages="agent" is not allowed in stage "{0}" as
ModelValidatorImpl.ToolsInNestedStages="tools" is not allowed in stage "{0}" as it contains parallel stages
ModelValidatorImpl.NoNestedWithinNestedStages=Parallel stages or branches can only be included in a top-level stage.

WhenConditionalValidator.changelog.missingParameter=Changelog is missing required parameter "pattern".
WhenConditionalValidator.changelog.badPattern="{0}" is not a valid regular expression. {1}

ModelInterpreter.NoNodeContext=Attempted to execute a step that requires a node context while 'agent none' was specified. Be sure to specify your own 'node { ... }' blocks when using 'agent none'.
Expand Up @@ -160,6 +160,39 @@ public void whenChangeset() throws Exception {
.go();
}

@Test
public void whenChangesetMoreCommits() throws Exception {
//TODO JENKINS-46086 First time build always skips the changelog
final ExpectationsBuilder builder = expect("when/changelog", "changeset")
.logContains("Hello", "Stage 'Two' skipped due to when conditional", "Warning, empty changelog. Probably because this is the first build.")
.logNotContains("JS World");
builder.go();

builder.resetForNewRun(Result.SUCCESS);

sampleRepo.write("somefile.txt", "//fake file");
sampleRepo.git("add", "somefile.txt");
sampleRepo.git("commit", "--message=Irrelevant");

sampleRepo.write("webapp/js/somecode.js", "//fake file");
sampleRepo.git("add", "webapp/js/somecode.js");
sampleRepo.git("commit", "--message=files");
sampleRepo.write("webapp/js/somecode.js", "//same file");
sampleRepo.git("add", "webapp/js/somecode.js");
sampleRepo.git("commit", "--message=same");

sampleRepo.write("somefile2.txt", "//fake file");
sampleRepo.git("add", "somefile2.txt");
sampleRepo.git("commit", "--message=Irrelevant");

builder.logContains("Hello", "JS World")
.logNotContains(
"Stage 'Two' skipped due to when conditional",
"Warning, empty changelog.",
"Examining changelog from all builds of this change request.")
.go();
}

@Test
public void whenChangesetPR() throws Exception {
//TODO JENKINS-46086 First time build "always" skips the changelog when git, not when mock
Expand Down Expand Up @@ -234,6 +267,40 @@ public void whenChangelog() throws Exception {
.go();
}

@Test
public void whenChangelogMoreCommits() throws Exception {
//TODO JENKINS-46086 First time build always skips the changelog
final ExpectationsBuilder builder = expect("when/changelog", "changelog")
.logContains("Hello", "Stage 'Two' skipped due to when conditional", "Warning, empty changelog. Probably because this is the first build.")
.logNotContains("Dull World");
builder.go();

builder.resetForNewRun(Result.SUCCESS);

sampleRepo.write("something.txt", "//fake file");
sampleRepo.git("add", "something.txt");
sampleRepo.git("commit", "-m", "Irrelevant");

sampleRepo.write("something.txt", "//slightly bigger fake file");
sampleRepo.git("add", "something.txt");
sampleRepo.git("commit", "-m", "Some title that we don't care about\n\nSome explanation\n[DEPENDENCY] some-app#45");

sampleRepo.write("other.txt", "//fake file");
sampleRepo.git("add", "other.txt");
sampleRepo.git("commit", "-m", "You should not care");

builder.logContains("Hello", "Dull World")
.logNotContains("Stage 'Two' skipped due to when conditional", "Warning, empty changelog.")
.go();
}

@Test
public void whenChangelogBadRegularExpression() throws Exception {
expect(Result.FAILURE,"when/changelog", "badRegularExpression")
.logContains("\"{\"user_id\" : 24}\" is not a valid regular expression.")
.logNotContains("Hello,", "Dull World").go();
}

@Test
public void whenChangelogPR() throws Exception {
//TODO JENKINS-46086 First time build "always" skips the changelog when git, not when mock
Expand Down
@@ -0,0 +1,49 @@

/*
* 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.
*
*/

pipeline {
agent {
label "here"
}
stages {
stage("One") {
steps {
echo "Hello, you should not see this."
}
}
stage("Two") {
when {
changelog '{"user_id" : 24}'
}
steps {
script {
echo "Dull World"
}

}
}
}
}

0 comments on commit c21c3d6

Please sign in to comment.