Skip to content

Commit

Permalink
feat: support allOf in a when context
Browse files Browse the repository at this point in the history
  • Loading branch information
garethjevans committed Mar 2, 2021
1 parent ce22e78 commit e04e686
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 20 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.lesfurets.jenkins.unit.declarative

import org.springframework.util.AntPathMatcher

import static groovy.lang.Closure.DELEGATE_FIRST

class AllOfDeclaration extends WhenDeclaration {

List<String> branches = []
List<Boolean> expressions = []
List<AnyOfDeclaration> anyOfs = []

def branch(String name) {
this.branches.add(name)
}

def expression(Closure closure) {
this.expressions.add(closure)
}

def anyOf(@DelegatesTo(strategy = DELEGATE_FIRST, value = AnyOfDeclaration) Closure closure) {
this.anyOfs.add(createComponent(AnyOfDeclaration, closure))
}

def expressions(Object delegate) {
return this.expressions.collect {executeWith(delegate, it)}.every()
}

def anyOf(Object delegate) {
return this.anyOfs.collect {it.execute(delegate)}
}

Boolean execute(Object delegate) {
def results = []

AntPathMatcher antPathMatcher = new AntPathMatcher()

if (this.branches.size() > 0) {
branches.each { branch ->
results.add(antPathMatcher.match(branch, delegate.env.BRANCH_NAME))
}
}

if (this.expressions.size() > 0) {
results.add(expressions(delegate))
}

if (this.anyOfs.size() > 0) {
results.addAll(anyOf(delegate))
}

return results.every()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package com.lesfurets.jenkins.unit.declarative
import static com.lesfurets.jenkins.unit.declarative.GenericPipelineDeclaration.executeWith
import org.springframework.util.AntPathMatcher

import static groovy.lang.Closure.DELEGATE_FIRST


class AnyOfDeclaration extends WhenDeclaration {

List<String> branches = []
List<Boolean> expressions = []
List<AllOfDeclaration> allOfs = []

def branch(String name) {
this.branches.add(name)
Expand All @@ -17,32 +20,37 @@ class AnyOfDeclaration extends WhenDeclaration {
this.expressions.add(closure)
}

def expressions(delegate) {
def exp_result;
for (def exp in this.expressions) {
exp_result = executeWith(delegate, exp)
if (exp_result) {
return true
}
}
return false
def allOf(@DelegatesTo(strategy = DELEGATE_FIRST, value = AllOfDeclaration) Closure closure) {
this.allOfs.add(createComponent(AllOfDeclaration, closure))
}

def allOf(Object delegate) {
return this.allOfs.collect {it.execute(delegate)}
}

def expressions(Object delegate) {
return this.expressions.collect {executeWith(delegate, it)}.any()
}

Boolean execute(Object delegate) {
boolean br = false;
boolean exp = false;
AntPathMatcher antPathMatcher = new AntPathMatcher();
def results = []

AntPathMatcher antPathMatcher = new AntPathMatcher()

if (this.branches.size() > 0) {
for(def branch in this.branches) {
br = br || antPathMatcher.match(branch, delegate.env.BRANCH_NAME)
branches.each { branch ->
results.add(antPathMatcher.match(branch, delegate.env.BRANCH_NAME))
}
}

if (this.expressions.size() > 0) {
exp = expressions(delegate)
results.add(expressions(delegate))
}

if (this.allOfs.size() > 0) {
results.addAll(allOf(delegate))
}

return exp || br
return results.any()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import static groovy.lang.Closure.DELEGATE_FIRST

class WhenDeclaration extends GenericPipelineDeclaration {

AllOfDeclaration allOf
AnyOfDeclaration anyOf
NotDeclaration not
Boolean buildingTag
Expand All @@ -19,7 +20,11 @@ class WhenDeclaration extends GenericPipelineDeclaration {

private static Pattern getPatternFromGlob(String glob) {
// from https://stackoverflow.com/a/3619098
return Pattern.compile('^' + Pattern.quote(glob).replace('*', '\\E.*\\Q').replace('?', '\\E.\\Q') + '$');
return Pattern.compile('^' + Pattern.quote(glob).replace('*', '\\E.*\\Q').replace('?', '\\E.\\Q') + '$')
}

def allOf(@DelegatesTo(strategy = DELEGATE_FIRST, value = AllOfDeclaration) Closure closure) {
this.allOf = createComponent(AllOfDeclaration, closure)
}

def anyOf(@DelegatesTo(strategy = DELEGATE_FIRST, value = AnyOfDeclaration) Closure closure) {
Expand Down Expand Up @@ -56,9 +61,13 @@ class WhenDeclaration extends GenericPipelineDeclaration {
boolean branchCheck = true
boolean tagCheck = true
boolean envCheck = true
boolean allOfCheck = true
boolean anyOfCheck = true
boolean notCheck = true

if (allOf) {
allOfCheck = allOf.execute(delegate)
}
if (anyOf) {
anyOfCheck = anyOf.execute(delegate)
}
Expand All @@ -69,7 +78,7 @@ class WhenDeclaration extends GenericPipelineDeclaration {
expressionCheck = executeWith(delegate, expression)
}
if (branch) {
AntPathMatcher antPathMatcher = new AntPathMatcher();
AntPathMatcher antPathMatcher = new AntPathMatcher()
branchCheck = antPathMatcher.match(branch, delegate.env.BRANCH_NAME)
}
if (buildingTag) {
Expand All @@ -83,7 +92,7 @@ class WhenDeclaration extends GenericPipelineDeclaration {
envCheck = (val == envValue)
}

return expressionCheck && branchCheck && tagCheck && envCheck && anyOfCheck && notCheck
return expressionCheck && branchCheck && tagCheck && envCheck && allOfCheck && anyOfCheck && notCheck
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,169 @@ class TestDeclarativePipeline extends DeclarativePipelineTest {
assertJobStatusSuccess()
}

@Test void when_allOf_expression() throws Exception {
addEnvVar('SHOULD_EXECUTE', 'true')
addEnvVar('SHOULD_ALSO_EXECUTE', 'true')
runScript('AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing allOf with expression')
assertJobStatusSuccess()
}

@Test void when_allOf_expression_first_false() throws Exception {
addEnvVar('SHOULD_EXECUTE', 'false')
addEnvVar('SHOULD_ALSO_EXECUTE', 'true')
runScript('AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example allOf expression')
assertJobStatusSuccess()
}

@Test void when_allOf_expression_second_false() throws Exception {
addEnvVar('SHOULD_EXECUTE', 'true')
addEnvVar('SHOULD_ALSO_EXECUTE', 'false')
runScript('AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example allOf expression')
assertJobStatusSuccess()
}

@Test void when_allOf_expression_both_false() throws Exception {
addEnvVar('SHOULD_EXECUTE', 'false')
addEnvVar('SHOULD_ALSO_EXECUTE', 'false')
runScript('AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example allOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_1() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'false')
addEnvVar('OPTIONAL_4', 'false')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_2() throws Exception {
addEnvVar('OPTIONAL_1', 'true')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'false')
addEnvVar('OPTIONAL_4', 'false')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_3() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'true')
addEnvVar('OPTIONAL_3', 'false')
addEnvVar('OPTIONAL_4', 'false')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_4() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'true')
addEnvVar('OPTIONAL_4', 'false')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_5() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'false')
addEnvVar('OPTIONAL_4', 'true')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_6() throws Exception {
addEnvVar('OPTIONAL_1', 'true')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'true')
addEnvVar('OPTIONAL_4', 'false')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_allOf_anyOf_expression_7() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'true')
addEnvVar('OPTIONAL_3', 'false')
addEnvVar('OPTIONAL_4', 'true')
runScript('Nested_AllOf_And_AnyOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing nested when allOf anyOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_anyOf_allOf_expression() throws Exception {
addEnvVar('OPTIONAL_1', 'true')
addEnvVar('OPTIONAL_2', 'true')
addEnvVar('OPTIONAL_3', 'true')
runScript('Nested_AnyOf_And_AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing nested when anyOf allOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_anyOf_allOf_expression_2() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'true')
addEnvVar('OPTIONAL_3', 'true')
runScript('Nested_AnyOf_And_AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing nested when anyOf allOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_anyOf_allOf_expression_3() throws Exception {
addEnvVar('OPTIONAL_1', 'true')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'true')
runScript('Nested_AnyOf_And_AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing nested when anyOf allOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_anyOf_allOf_expression_4() throws Exception {
addEnvVar('OPTIONAL_1', 'true')
addEnvVar('OPTIONAL_2', 'true')
addEnvVar('OPTIONAL_3', 'false')
runScript('Nested_AnyOf_And_AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Executing nested when anyOf allOf expression')
assertJobStatusSuccess()
}

@Test void when_nested_when_anyOf_allOf_expression_5() throws Exception {
addEnvVar('OPTIONAL_1', 'false')
addEnvVar('OPTIONAL_2', 'false')
addEnvVar('OPTIONAL_3', 'false')
runScript('Nested_AnyOf_And_AllOf_Jenkinsfile')
printCallStack()
assertCallStack().contains('Skipping stage Example nested when anyOf allOf expression')
assertJobStatusSuccess()
}

@Test void when_branch() throws Exception {
addEnvVar('BRANCH_NAME', 'production')
runScript('Branch_Jenkinsfile')
Expand Down
25 changes: 25 additions & 0 deletions src/test/jenkins/jenkinsfiles/AllOf_Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pipeline {
agent any
stages {
stage('Example Build') {
steps {
echo 'Hello World'
}
}
stage('Example allOf expression') {
when {
allOf {
expression {
return env.SHOULD_EXECUTE == 'true'
}
expression {
return env.SHOULD_ALSO_EXECUTE == 'true'
}
}
}
steps {
echo 'Executing allOf with expression'
}
}
}
}
Loading

0 comments on commit e04e686

Please sign in to comment.